Thread killen.



  • Hallo,
    Mein Problem ist folgendes: Ich erstelle einen neuen Thread, der in der Funktion run() in ein Excel Sheet schreibt. Innerhalb von run() wird ein neues Objekt der Klasse Excel erzeugt. Excel* excel = new Excel().
    Am Ende von run() wird delete excel aufgerufen. (Soweit alles logisch)

    Listctrl::run()
    {
    Excel* excel = new Excel();
    .
    .
    delete excel;
    }
    

    Nun möchte ich aber den Fall abfangen, dass der Benutzer die dazugehörige Oberfläche schließt und somit den Thread beendet.
    Das Problem hierbei ist das beenden des Worker-Threads. Da der Thread nicht bis zum Ende von run() läuft, wird delete excel nicht aufgerufen und somit bleibt das Excel Sheet im Arbeitsspeicher vorhanden (Str+Alt+Entf; 50 Mal Excel geöffnet). Das Problem ist, dass Excel nur innerhalb von run() bekannt ist und selbst eine Adress-Kopie auf ein global erzeugtes Excel-Objekt nicht funktioniert.
    Hat jemand eine Idee und kann mir helfen??

    lg



  • Warum erstellst Du überhaupt das Objekt mit new?



  • Warum keinen auto_ptr ? Der sollte das doch beim killen zerstören.



  • Also die Frage weshalb ich einen Pointer verwende ist sehr gut 😃 😃 Wohl Macht der Gewohnheit. Werde es dann gleich mal ausprobieren. Von einem auto_ptr hab ich ehrlich gesagt noch nie etwas gehört 😞
    Was macht der genau?
    Nochmals vielen Dank!!



  • Ok, das mit dem Objekt statt Pointer hat nicht funktioniert. In Excel erzeuge ich ein neues Objekt mit p_Excel = new QAxObject("Excel.Application",this);
    Die Anwendung ist dann noch immer im Speicher. 😞



  • Threads killen == böse.
    Du musst den Thread kontrolliert abbrechen lassen.

    D.h. von außen kommt eine Aufforderung zum beenden, der Thread liest in gewissen (kurzen) Zeitabständen synchronisiert die Abbruchaufforderung und beendet sich ggf. selbst.



  • std::auto_ptr schlagt euch aus dem Kopf, sowohl du, Tobi, als auch du Hannes.

    Gilt als veraltet, weil er intern eine Art move-Semantik anwendet und trotzdem kopierbar ist.

    Nimm std::unique_ptr aus memory . Der zerstoert (wie auto_ptr ) den Speicher, auf den er zeigt, beim verlassen des Scopes, in dem er deklariert wurde. Praktisch also eine RAII-Huelle fuer dynamisch reservierten Speicher.

    Zu deinem Problem: Wieso klappt Objekt statt Pointer nicht? Fehlermeldung?



  • Ich werde sofort nach std::auto_ptr suchen....
    Das mit dem Objekt funktionert nicht, da ich im Konstruktor von Excel ein neues QAxObject erzeuge und nur der Destruktor von Excel dieses wieder löschen kann. Innerhalb von run() rufe ich ja mit delete excel den Destruktor automatisch auf. Nur komme ich eben nicht bis dahin wenn ich den Thread vorher abbreche.

    #include "excel.h"
    
    Excel::Excel(QObject *parent):QObject(parent)
    {
        lineVari = 2; //Soll bei Zelle A2 beginnen;
        lineDir = 2;
        p_Excel = new QAxObject("Excel.Application",this);
        p_Workbooks = p_Excel->querySubObject("WorkBooks");
        p_Workbook = p_Workbooks->querySubObject("Add()");
        p_Sheets = p_Workbook->querySubObject("Worksheets");
        p_Sheet1 = p_Sheets->querySubObject("Item(int)",1);
        p_Sheet2 = p_Sheets->querySubObject("Item(int)",2);
        p_Sheet1->dynamicCall("SetName(const QString&)", QVariant("Variables"));
        p_Sheet2->dynamicCall("SetName(const QString&)", QVariant("Software"));
        AddHeaderVariable();
        AddHeaderDirectory();
    }
    
    void Excel::AddHeaderVariable()
    {
        SetFontBold("A1", "H1", true, true);
        SetCellColor("A1","H1",27,true);
        SetFontSize("A1", "H1", 16,true);
        Write("A1","Group",true);
        Write("B1","Ctrl_Group",true);
        Write("C1","Monitor",true);
        Write("D1","Device",true);
        Write("E1","Alias",true);
        Write("F1","Variable",true);
        Write("G1","Value",true);
        Write("H1","Date",true);
    }
    
    void Excel::AddHeaderDirectory()
    {
        SetFontBold("A1", "H1", true, false);
        SetCellColor("A1","H1",27,false);
        SetFontSize("A1", "H1", 16,false);
        Write("A1","Group",false);
        Write("B1","Ctrl_Group",false);
        Write("C1","Monitor",false);
        Write("D1","Processor",false);
        Write("E1","Device",false);
        Write("F1","Alias",false);
        Write("G1","Software",false);
        Write("H1","Date",false);
    }
    
    void Excel::Write(const QString& cell, const QString& value, bool vari)
    {
        if(vari==true)
        {
            QAxObject *p_Entry = p_Sheet1->querySubObject("Range(const QVariant&)", QVariant(cell));
            p_Entry->dynamicCall("SetValue(const QVariant&)", QVariant(value));
            Autosize(true);
        }
        else
        {
            QAxObject *p_Entry = p_Sheet2->querySubObject("Range(const QVariant&)", QVariant(cell));
            p_Entry->dynamicCall("SetValue(const QVariant&)", QVariant(value));
            Autosize(false);
        }
    }
    
    void Excel::SetCellColor(const QString& startCell, const QString& endCell, int color, bool vari)
    {
        if(vari == true)
        {
            QAxObject *p_Range = p_Sheet1->querySubObject("Range(const QString&, const QString&)", QString(startCell), QString(endCell));
            p_Range->querySubObject("Interior")->setProperty("ColorIndex",color);
        }
        else
        {
            QAxObject *p_Range = p_Sheet2->querySubObject("Range(const QString&, const QString&)", QString(startCell), QString(endCell));
            p_Range->querySubObject("Interior")->setProperty("ColorIndex",color);
        }
    }
    
    void Excel::SetFontSize(const QString& startCell, const QString& endCell, int size, bool vari)
    {
        if(vari == true)
        {
            QAxObject *p_Range = p_Sheet1->querySubObject("Range(const QString&, const QString&)", QString(startCell), QString(endCell));
            p_Range->querySubObject("Font")->setProperty("Size",size);
        }
        else
        {
            QAxObject *p_Range = p_Sheet2->querySubObject("Range(const QString&, const QString&)", QString(startCell), QString(endCell));
            p_Range->querySubObject("Font")->setProperty("Size",size);
        }
    }
    
    void Excel::SetFontBold(const QString& startCell, const QString& endCell, bool bold, bool vari)
    {
        if(vari == true)
        {
            QAxObject *p_Range = p_Sheet1->querySubObject("Range(const QString&, const QString&)", QString(startCell), QString(endCell));
            p_Range->querySubObject("Font")->setProperty("Bold",bold);
        }
        else
        {
            QAxObject *p_Range = p_Sheet2->querySubObject("Range(const QString&, const QString&)", QString(startCell), QString(endCell));
            p_Range->querySubObject("Font")->setProperty("Bold",bold);
        }
    }
    
    void Excel::Autosize(bool vari)
    {
        if(vari == true)
        {
            QAxObject* p_Columns = p_Sheet1->querySubObject("Columns(const QString&)", "a:z");
            p_Columns->dynamicCall("AutoFit()");
        }
        else
        {
            QAxObject* p_Columns = p_Sheet2->querySubObject("Columns(const QString&)", "a:z");
            p_Columns->dynamicCall("AutoFit()");
        }
    }
    
    void Excel::SetVisible(bool visible)
    {
        p_Excel->dynamicCall("SetVisible(bool)",visible);
    }
    
    void Excel::WriteLine(const QString& group, const QString& ctrlgroup, const QString& monitor,
                   const QString& processor, const QString& device, const QString& alias, const QString& variable,
                   const QString& value, bool vari)
    {
        if(vari==true)
        {
            QString Line = QString::number(lineVari);
            QDateTime dateTime = QDateTime::currentDateTime();
            QString dTime = dateTime.toString();
            Write("A"+Line,group,true);
            Write("B"+Line,ctrlgroup,true);
            Write("C"+Line,monitor,true);
            Write("D"+Line,device,true);
            Write("E"+Line,alias,true);
            Write("F"+Line,variable,true);
            Write("G"+Line,value,true);
            Write("H"+Line,dTime,true);
            lineVari++;
        }
        else
        {
            QString Line = QString::number(lineDir);
            QDateTime dateTime = QDateTime::currentDateTime();
            QString dTime = dateTime.toString();
            Write("A"+Line,group,false);
            Write("B"+Line,ctrlgroup,false);
            Write("C"+Line,monitor,false);
            Write("D"+Line,processor,false);
            Write("E"+Line,device,false);
            Write("F"+Line,alias,false);
            Write("G"+Line,value,false);
            Write("H"+Line,dTime,false);
            lineDir++;
        }
    }
    
    Excel::~Excel()
    {
         p_Sheet2->~QAxObject();
         p_Sheet1->~QAxObject();
         p_Sheets->~QAxObject();
         p_Workbook->~QAxObject();
         p_Workbooks->~QAxObject();
         p_Excel->~QAxObject();
         qDebug()<<"Excel wird vernichtet";
    }
    


  • HannesKannNix schrieb:

    Excel::~Excel()
    {
         p_Sheet2->~QAxObject();
         p_Sheet1->~QAxObject();
         p_Sheets->~QAxObject();
         p_Workbook->~QAxObject();
         p_Workbooks->~QAxObject();
         p_Excel->~QAxObject();
         qDebug()<<"Excel wird vernichtet";
    }
    

    Durch direkte Destruktoraufrufe wird der Speicher, in dem das betreffende Objekt liegt, nicht freigegeben. Das ist nur sinnvoll, wenn man es vorher mit placement-new konstruiert hat und sich eh selbst darum kümmert. Das ist hier nicht der Fall.

    Wenn der QAxObject-Kram einigermaßen sinnvoll gebaut ist, sollte da

    Excel::~Excel()
    {
         delete p_Excel;
         qDebug()<<"Excel wird vernichtet";
    }
    

    stehen. Das unter der Annahme, dass querySubObject einen nicht-besitzenden Zeiger zurückgibt.



  • Es ist wirklich wie verhext. Also ich habe den Destruktor vor Excel geändert mit delete p_Excel und es hat nichts geholfen.
    Auch folgender Versuch hat nicht funktioniert:

    void ListCtrl::run()
    {
        std::auto_ptr<Excel> excel(new Excel());
    .
    .
        delete excel;
    }
    

    wird die run() Funktion vor delete unterbrochen, bleibt ein Excel-Prozess am Leben. Das gibt es doch nicht. Werde es jetzt mal mit unique_ptr versuchen. Leider muss ich da erst herausbekommen, wie ich den in QT bekomme.
    Ich danke euch trotzdem vielmals.



  • THREAD KILLEN == BÖSE!
    BEENDE DEN SCHEIß THREAD KONTROLLIERT!

    Außerdem ist delete offensichtlich überflüssig, wenn schon ein SmartPointer im Einsatz ist.



  • So, habe jetzt versucht den Thread nicht zu killen, sondern ihn intern zu "überspringen":

    void ListCtrl::run()
    {
    while(!Stop)
    {
    .
    .
    .
    }
    delete excel;
    }
    

    Leider funktioniert auch das nicht wie geplant. Wenn ich von außen über eine Set-Methode Stop auf true setze, dann läuft der Thread einfach weiter und wird erst zu true, sobald der Thread abgehandelt ist. Wie kann das sein???
    Was mach ich falsch??



  • Hallo,
    ich habe es noch immer nicht geschafft den Excel Prozess zu stoppen.
    Er läuft einfach im Hintergrund weiter, egal was ich mache 😞



  • Hallo,

    Du hast eine vollkommen falsche Herangehensweise, scheint mir.

    Im Normalfall beendet man einen Thread (wie schon mehrfach hier erwähnt) geregelt. Du hast ja das Stop-Flag und das ist auch gut so, Du brauchst jedoch an keiner Stelle innerhalb von Excel das delete aufrufen. Klassen sollten keinen Suizid begehen.

    Und dann regelt man es in QT normalerweise so, dass man das finished()-Signal (oder ähnlicher Name) zum delete verbindet. Und man sollte auch definitiv moveToThread in QT benutzen. Ich finde gerade den Artikel nicht, aber da gibt es so ein zwei Dinge zu beachten, die Du vorher in Erfahrung bringen solltest.



  • Inzwischen habe ich eingesehen, dass es besser ist den Thread NIE zu killen. Ich warte auf das finished-Signal des Threads und lösche anschließend die im Thread dynamisch erstellten Komponenten.
    Ich habe jetzt aber das Problem, dass einfach zu viel Zeit vergeht, bis der Thread zu Ende ist. Im Thread werden per TCP/IP Verbindungen auf und abgebaut, wodurch mehrere Sekunden vergehen können.
    Gibt es eine Möglichkeit den Thread vorzeitig zu beenden ohne ihn zu killen?



  • Davon ist halt nichts sicher. Das grundsätzliche Vorgehen ist eben zu warten. QT verzögert automatisch, sodass es halt länger dauert, bis das Programm wirklich schließt. Halte ich persönlich aber für sinnvoll.

    Wenn eine einzelne Funktion ein paar Sekunden dauern kann, kannst Du vielleicht schauen, ob es davon eine nicht-blockierende Variante gibt.

    Ansonsten: wo siehst Du konkret das Problem, dass es ein paar Sekunden dauert?

    Edit: Ich habe einen wiki-Eintrag gefunden, der bzgl. QT sehr viel hilft. Du hattest ja Mal davon geschrieben. Wenn Du QT und Threads nutzt, hilft er vielleicht, in jedem Fall ist er wertvoll: http://qt-project.org/wiki/Threads_Events_QObjects



  • HannesKannNix schrieb:

    Wenn ich von außen über eine Set-Methode Stop auf true setze, dann läuft der Thread einfach weiter

    Wie ist das ganze synchronisiert? Ohne Synchronisierung zwischen den Threads hast du keine Garantie, dass dein Thread die von einem anderem Thread gesetzte Variable überhaupt jemals wahrnimmt.

    Ich empfehle ein Buch über nebenläufige Programmierung.


Log in to reply