Thread beendet nicht trotz Aufruf von TThread::Terminate()



  • Manfred Schmidtke schrieb:

    Terminate() setzt lediglich FTerminated und damit die Eigenschaft Terminated auf true.

    Was macht man eigentlich, wenn das eben nicht passiert? 😕 Wenn ich z.B. bei OnCloseQuery eines Formulars Terminate aufrufe, stört das die Eigenschaft Terminated überhaupt nicht. Rufe ich Terminate allerdings von nem Button auf, klappt es. (Frage übrigens an Alle, nicht direkt an M.Schmidtke gerichtet! 🙂 )



  • Wieso sollte das nicht passieren?!? Ist nur die Frage, ob dein Thread darauf reagiert - und dafür mußt Du selbst sorgen... Das kann natürlich nicht funktionieren, so lange der Thread suspended ist.



  • Und wie muss ich dafür sorgen? Falls du meinst, ich muss bei Execute abfragen, was in Terminated drinsteht: Hab ich gemacht.
    Suspended ist er auch nicht. (Das das böse ist, habe ich hier schon rausgefunden. 🙂 )



  • Damit ist egientlich dafür gesorgt. Hast du ein konkretes Problem?



  • Kannst Du das Problem genauer beschreiben? Eventuell mußt Du einfach nur, nach dem Senden des Terminate() an den Thread, mittels WaitFor() auf das tatsächliche Beenden und Freigeben des Threads warten.



  • Da kann ich warten, bis ich schwarz werde. Ich wäre froh, wenn ich es genauer beschreiben könnte. Aber der Befehl Terminate() wird aufgerufen, die Eigenschaft Terminated bleibt false. Die while(!Terminated)-Schleife läuft ewig weiter, Execute wird nicht verlassen.
    Passiert aber nur, wenn ich das Terminate() im OnClose oder OnCloseQuery des Elternformulars (das den Thread beinhaltet, ist übrigens nicht das Hauptformular --> Thread wird nicht gelöscht) aufrufe. Bei nem Buttonklick, der Terminate() aufruft gibts kein Problem.

    Ja, ich weiß, es ist schwer nachzuvollziehen, weil vollkommen unlogisch und sinnlos. Ist aber so.



  • Du musst dem Thread natürlich auch zeit geben sich zu beenden. Sprich ein WaitFor auf den Thread ist unerlässlich bei dem Close-Event! (o;



  • Er beendet sich ja nicht! 😞 Und wie gesagt: Beim schließen des Forms wird der Thread nicht gelöscht, also ist ihm ziemlich egal, ob das Form nun sofort oder zwei Sekunden später geschlossen (nicht gelöscht!) wird. (Abgesehen von einer kleinen Zugriffsverletzung auf ein Image-Canvas, aber das gehört hier nicht her. :))



  • Bei mir funktioniert das so:
    (MyThreads ist ein TThreadList*, die Threads werden im Destruktor der Form gelöscht.)

    void __fastcall TForm1::FormCloseQuery(TObject *Sender, bool &CanClose)
    {
    	bool DoClose;
    	if (MyThreads)
    	{
    		try
    		{
    			TList* pList = MyThreads->LockList();
    			if (pList->Count)
    			{
    				if (Application->MessageBoxA("Abbrechen?", "Abbruchanforderung", MB_YESNO) == IDYES)
    				{
    					for (int i = 0; i < pList->Count; i++)
    					{
    						TTestThread* pThread = (TTestThread*) pList->Items[i];
    						if (!pThread->Suspended)
    							pThread->Suspend();
    					}
    					for (int i = 0; i < pList->Count; i++)
    					{
    						TTestThread* pThread = (TTestThread*) pList->Items[i];
    						pThread->Priority = tpNormal;
    						if (pThread->Suspended)
    							pThread->Resume();
    						pThread->Terminate();
    						pThread->WaitFor();
    					}
    					DoClose = true;
    				}
    				else
    				{
    					DoClose = false;
    				}
    			}
    			else
    			{
    				DoClose = true;
    			}
    		}
    		__finally
    		{
    			MyThreads->UnlockList();
    		}
    	}
    	CanClose = DoClose;
    }
    


  • Hmm... hübsch. 🤡 Nun... Resume, Terminate und WaitFor haben wir ja bereits geklärt. LockList dürfte bei einem einzigen Thread (abgesehen vom Hauptthread) nicht viel ändern. Die Priorität ist bei mir auch Normal. Wüsste also nicht, wo ich meinen Code anpassen müsste. 😞



  • Naja z.b aus "if (thread->suspended)" ne "while(thread->supsended)" machen. Wieso das die richtige Lösung ist und nicht das if, entnimmt man der Beschreibung von Suspend resp. Resume.

    Wann wird denn dein Thread gelöscht? Bzw. Wer ist besitzer des Threads?



  • Ah, die Verschachtelung. Gut, jetzt habe ich das auch mal kapiert. 🙂

    Aber der Thread steht nicht. Er läuft immer munter weiter, ruft immer wieder das in der while(!Terminated) auf und lässt sich ums verrecken nicht davon abbringen.

    Der Besitzer des Threads ist das (unter-)Formular, um deren OnClose-Routine es geht. Der Thread wird beim Löschen dieses Formulars mitgelöscht. Passiert aber erst beim Beenden der Anwendung.

    (Irgendwie steigt in mir dieser komische Drang, mich in die Ecke zu setzen und ein bisschen zu weinen... das macht alles so gar keinen Sinn... 😞 )



  • Aber der Aufruf von TThread::Terminate() wird an der Stelle ausgeführt, an der Du es erwartest? Mal mit dem Debugger Haltepunkte in der OnCloseQuery gesetzt?



  • Stimmt... Weinen macht keinen Sinn. (o: Lass dir erklären wie das Ganze Funktioniert:

    Vorab sei gesagt: Die Kapselung der Thread-Funktion in eine Klasse ist ein Murks. Und über genau den fällst du jetzt.

    Wenn man die ERstellung eines Threads WinAPI-Seitig betrachtet, dann erwartet die API-Funktion einen Funktionszeiger auf deine Thread-Routine.

    Leider kann man in C++ (verständlicherweise) keine Funktionszeiger auf Klassenfunktionen machen. Ausnahme: Statische Memberfunktionen.
    Damit man jetzt eine schöne Wrapper-Klasse machen kann, muss man zu nem Murks greifen: Man macht eine Statische Funktion namens Run. Freundlicherweise kann man beim Festlegen des Callbacks mit der API-Version einen Zeiger mit übergeben. In diesen Zeiger packt man den Zeiger auf die Klasse (this).

    Wird nun aus dem Betriebssystem Run aufgerufen, macht run nichts weiter als einen Cast des Zeigers auf die ursprüngliche Thread-Klasse und ein Aufruf der Execute-Funktion.

    Was passiert jetzt wenn du die Klasse einfach löschst? - Das Problem hier ist das das Flag Terminate() zu ungültigem Speicher (übrigens wie der Rest des Programms) wird.

    Deshalb musst du mit dem delete solange warten, bis der Thread wirklich beendet ist.

    -junix



  • So, wie es aussieht, kann ich mir die Tränen nun aus den Augen wischen. Aber aus der Ecke stehe ich noch nicht auf, sondern verstecke mich darin vor Scham. In einer Funktion, die das Aussehen des Formulars (und auch die Initialwerte) wieder auf die Startwerte zurücksetzen sollte befand sich diese (und in diesem Zusammenhang auch NUR diese) Zeile:

    this->RefThread = new TRefThread(true);
    

    Damit wollte ich wohl den Thread auch auf Startwerte zurücksetzen. Habe aber tatsächlich den ersten Thread weiterlaufen lassen und den Zeiger auf einen Neuen zeigen lassen. Habe also einen Thread terminiert, der nicht lief und einen Thread weiterlaufen lassen, den ich dann stoppen wollte aber nicht mehr konnte. Tja, keine Ahnung, wie ich das nun wieder geschafft habe. Öh... das war gar nicht ich, das ist der einarmige, einbeinige, einäugige Mann gewesen! (Nein, ich habe natürlich nichts gegen einarmige, einbeinige, einäuigige Menschen.)

    Danke für eure Hilfe, jetzt habe ich wenigstens zusätzliches Wissen über Threads gesammelt. Ja, ihr dürft mich hassen, ich mache es ja auch. 🙂



  • HEZ schrieb:

    Ja, ihr dürft mich hassen, ich mache es ja auch. 🙂

    Nö, hasse nicht... nur ein bischen AUslachen vielleicht 😉 (du weisst wies gemeint ist)

    -junix


Log in to reply