Laden...

Sauberes beenden eines Threads

Erstellt von Midnight Run vor 12 Jahren Letzter Beitrag vor 12 Jahren 6.843 Views
Thema geschlossen
Midnight Run Themenstarter:in
80 Beiträge seit 2008
vor 12 Jahren
Sauberes beenden eines Threads

Hallo,
ich habe da eine grundlegende Frage zum Thema Threading. Um genauer zu sein wie beende ich einen Thread sauber inmitten seiner Arbeit.

Ich habe mir ein paar Sourcecodes angesehen und diese bedienen sich oft dem Methode Thread.Abort().

Im Internet, bei Stackoverflow.com, habe ich einen Beitrag gelesen indem dieses Vorgehen als falsch betitelt wurde, da die Abort Methode nur bei ernsten Fällen eingesetzt werden sollte.

Man sollte lieber mit einem Boolean arbeiten, der, falls er einen bestimmten Zustand erreicht hat(true/false) aus dem Thread springt.

Meine erste Frage ist daher, wie macht man es richtig ?

Ich arbeite oft nach diesem Muster :

Thread t = new Thread(delegate()
{
  // Code
});

t.Start();

while(t.IsAlive)
{
  Application.DoEvents();
}

Meine nächste Frage ist, wie beende ich einen Thread sauber, wenn der Benutzer plötzlich das Programm schließt. Im Debugmode erkenne ich das, dass Programm noch läuft, sprich der STop Button ist noch eingeblendet.

Das würde bedeuten, dass wenn der Benutzer dies tut auch immer noch der Thread bei ihm läuft, oder ?

Ich habe zur Zeit ein Programm das viele Daten über FTP bezieht und der Benutzer könnte einfach das Programm genau bei diesem Prozess schließen. Wie kann ich dann den Thread sauber stoppen ohne Thread.Abort() ?

Danke für eure Hilfe

T
2.224 Beiträge seit 2008
vor 12 Jahren

Hatte mir auch die Frage gestellt wie man sowas löst.
Dazu gibt es die Möglichkeit mit Application.ApplicationExit ein Event zu registrieren.

Application.ApplicationExit Event

Somit kannst du den Thread dann beim schließen der Anwendung sauber beenden.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

5.742 Beiträge seit 2007
vor 12 Jahren
  
while(t.IsAlive)  
{  
  Application.DoEvents();  
}  

Dieses Konstrukt ist wirklich alles andere als eine gute Idee!
Was willst du damit erreichen? Siehe [FAQ] Warum blockiert mein GUI?

Ansonsten gibt's ab .NET 4.0 das CancellationToken für gezielte Threadabbrüche.

Ansonsten: Mache aus deinem Thread ein Backgroundthread - dann wird er beim Beenden des Prozesses automatisch gestoppt.

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo Midnight Run,

dein Muster ist ganz böse, v.a. wegen DoEvents. Suche mal im Forum danach.

Ab .net 4.0 bietet sich Cancellation an. Für Hintergründe siehe .NET 4 Cancellation Framework dafür an.
Vorher hast du ein volatile bool-Feld das im Thread geprüft werden kann und wenn dieses true ist soll die Methode verlassen werden. Z.B. per return od. wenns eine Schleife ist per break

wie beende ich einen Thread sauber, wenn der Benutzer plötzlich das Programm schließt.

Wenn es ein Background-Thread ist wird dieser automatisch beendet. Nur Vordergrund-Threads halten die Anwendung am laufen.
Vordergrund-Threads werden mit new Thread erstellt, ohne dass die IsBackgroundThread-Eigenschaft auf true gesetzt wird.
Thread-Pool-Threads sind automatisch Background-Threads.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

296 Beiträge seit 2007
vor 12 Jahren

Hallo Midnight Run,

meiner Meinung nach wird ein Thread sauber beendet, nachdem die Methode die er ausführt zurückkommt.

Deshalb findest du auch teilweise Konstrukte wie:


void run() {
   while(!_stopThread) {
      [DoSth]

      if([condition])
         break;

      [DoSth]
   }
}

Für das beenden des Threads "von außen" wird die Variable _stopThread auf wahr gesetzt. Dabei ist natürlich klar, dass er seine derzeite iteration noch beendet. Dann kann man davon ausgehen, dass der Datenzustand noch konsistent ist, und nicht halb fertige Operationen ausgeführt wurden.

Mit Thread.Abort() wird in der Methode die besagte Exception geworfen. Die kannst du fangen und behandeln.

Um den Thread beim schließen des Programms zu beenden, kannst du ihn als "Background-Thread" flaggen. Das heißt er stirbt, wenn der Erzeuger stirbt (bpsw. dein GUI-Thread).

Ist dein Codeschnipsel da nur ein Beispiel oder machst du das 1:1 so? In dem Fall bringt dir Threading nichts, weil du nur die Arbeit in einen anderen Thread verlagerst und den derzeitigen Thread solange blockierst, wie dieser beschäftigt ist.

Midnight Run Themenstarter:in
80 Beiträge seit 2008
vor 12 Jahren

Hallo,
danke für eure zahlreichen Antworten. Ja, leider tue ich das 1:1 so, wie in meinem Beispiel, aber ich verspreche Besserung.

Ich arbeite leider mit .NET 3.5. Deswegen fallen die Lösungen mit .NET 4.0 weg.

Zu diesem Code habe ich eine Frage :


void run() {
   while(!_stopThread) {
      [DoSth]

      if([condition])
         break;

      [DoSth]
   }
}

Es wird ja eine komplette Iteration gewartet bis _stopThread true ist. Aber was ist wenn die Operationen innerhalb der Schleife viel Zeit in Anspruch nehmen.

Sollte man den Benutzer warten lassen ?

Und was ist wenn der Thread nicht aus Schleifenoperationen besteht. Sollte man dann in gewissen Abständen überprüfen ob der Boolean true ist ?

Danke

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo Midnight Run,

Es wird ja eine komplette Iteration gewartet

Nein - die Iteration wird ausgeführt. Gewartet wird nirgends.

Aber was ist wenn die Operationen innerhalb der Schleife viel Zeit in Anspruch nehmen.

Dann kann in der langen Operation an bestimmten Stellen auch _stopThread geprüft werden.

Sollte man den Benutzer warten lassen ?

Das ist eigentlich üblich, dass dann irgendwo irgendwie "Stopping Operation..." angezeigt wird.

Sollte man dann in gewissen Abständen überprüfen ob der Boolean true ist ?

Ist eine Möglichkeit.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

Midnight Run Themenstarter:in
80 Beiträge seit 2008
vor 12 Jahren

Hey,

Es wird ja eine komplette Iteration gewartet

Ich meinte, dass einmal die Iteration durchlaufen werden muss, bis wieder die Abfrage durchgeführt wird. Bei bspw. langen Datenübertragungen wäre dies mit einer langen Wartezeit verbunden.

Aber was ist wenn die Operationen innerhalb der Schleife viel Zeit in Anspruch nehmen.

Dann kann in der langen Operation an bestimmten Stellen auch _stopThread geprüft werden.

Das bedeutet wenn ich in meinem Thread mehrere Methoden aufrufe, sollte man auch diese mit einer Überprüfungen ausrüsten ? Mir würde das jetzt aus dem Kopf dann zu Komplex werden oder verstehe ich da was falsch ?

Danke für deine Antwort

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo Midnight Run,

Das bedeutet wenn ich in meinem Thread mehrere Methoden aufrufe, sollte man auch diese mit einer Überprüfungen ausrüsten ? Mir würde das jetzt aus dem Kopf dann zu Komplex werden oder verstehe ich da was falsch ?

Kommt darauf an wie feingranular du das Abbrechen/Beenden haben willst. Ich würde hier einfach vor jedem Methodenaufruf _stopThread prüfen und die aufgerufenen Methoden davon gar nix wissen lassen. Sonst wird es wirklich unnötig kompliziert.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

296 Beiträge seit 2007
vor 12 Jahren

Ich habe zur Zeit ein Programm das viele Daten über FTP bezieht und [...]

Sollte man den Benutzer warten lassen ?

Die zweite Möglichkeit wäre die Daten mit der Zeit nachzuliefern. Dann muss der Benutzer nicht warten, bis alles runtergezogen ist und kann mit den bestehenden Daten schon arbeiten.

Das bedeutet wenn ich in meinem Thread mehrere Methoden aufrufe, sollte man auch diese mit einer Überprüfungen ausrüsten ? Mir würde das jetzt aus dem Kopf dann zu Komplex werden oder verstehe ich da was falsch ?

Eine allgemein gültige Antwort hierzu gibts wahrscheinlich nicht, aber in der Regel ist das nicht oder nur bei wirklich zeitintensiven Operationen nötig und dann nicht unbedingt komplex.

Wo meinst du denn so eine Überprüfung zu benötigen?

Midnight Run Themenstarter:in
80 Beiträge seit 2008
vor 12 Jahren

Hi,

Wo meinst du denn so eine Überprüfung zu benötigen?

Ich habe öfters gehört, dass es guter Stil ist, seine Methoden klein zu halten, also eine spezifische Aufgabe zu erfüllen um die Übersicht zu behalten. Wenn ich mir nun eine komplexe Aufgabe vorstelle und diese in einem Thread bearbeiten möchte, treffe ich auf diese Problem.

Natürlich kann ich alles fein ausarbeiten, aber ihr kennt das sicher wenn man sich 1 - 2 Monate wieder an das Projekt setzt, dass man erstmal wieder davor steht wie der Ochs vorm Berg 😃

Ich bedanke mich für eure Antworten, danke ich werde mir alles nochmal ansehen 😉

Midnight Run Themenstarter:in
80 Beiträge seit 2008
vor 12 Jahren

Heißt das eigentlich, dass ich einfach ein Thread.IsBackground = true setzen könnte und somit einen Background Thread hätte ?

Das Application.DoEvents() nutze ich nur dafür um zu warten bis die arbeiten vorbei sind.

Wie entscheidet man den wann man einen Backgroundworker nutzen will, den Threadpool oder selbst den Thread erstellt ? Gibt es da so paar Dinge nach denen man sich richten kann ?

Gruß

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo Midnight Run,

Application.DoEvents()

streich das am besten aus deinem Wortschatz und verwende es nie. Warum kannst du über die Forensuche nachlesen.

nur dafür um zu warten

Warten ist in der UI auch nicht nötig. Verwende besser Ereignisse.

Beide Punkte kannst du in [FAQ] Warum blockiert mein GUI? auch nachlesen.

ein Thread.IsBackground = true setzen könnte und somit einen Background Thread hätte

Ja, aber das sollte nicht zum sauberen Beenden missbraucht werden. Siehs andersrum: du kannst einen Vordergrundthread machen der die Anwendung am Laufen hält bis der Thread fertig ist. Das ist der Sinn dahinter.

ThreadPool-Threads sind standardmäßig immer Backgroundthreads. Wie vorher schon erwähnt ein Vordergrundthread hält die Anwendung am Laufen und das kann manchmal Sinn machen (dazu ist es aber besser direkt einen neuen Thread mit new Thread zu erstellen, als einen ThreadPool-Thread auf IsBackground=true zu setzen).

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

Midnight Run Themenstarter:in
80 Beiträge seit 2008
vor 12 Jahren

Hey,
und die Background Worker Class ist einfach nur ein Wrapper um einen Backgroundthread ?

Dort gibt es ja schon die Ereignisse "OnProgress" und "OnCompleted".

Mir fällt gerade kein Szenario ein, wann eine Applikation mit einem Thread weiterlaufen sollte. Vielleicht um Inkonsistenz zu vermeiden ?

Gruß

5.742 Beiträge seit 2007
vor 12 Jahren

und die Background Worker Class ist einfach nur ein Wrapper um einen Backgroundthread ?

Ja, so kann man das sehen.

Mir fällt gerade kein Szenario ein, wann eine Applikation mit einem Thread weiterlaufen sollte. Vielleicht um Inkonsistenz zu vermeiden ?

Ja.
Einfaches Beispiel: Man lagert das Speichern einer Datei in einen Thread aus. Wenn dann die Anwendung beendet wird und der Thread automatisch terminiert wird, ist der Inhalt der Datei evtl. futsch.
Als Lösung muss erst gewartet werden, bis der Thread seine Arbeit fertig hat.

49.485 Beiträge seit 2005
vor 12 Jahren

Hallo Midnight Run,

Mir fällt gerade kein Szenario ein, wann eine Applikation mit einem Thread weiterlaufen sollte

wenn man den Thread sauber beenden will. Siehe Titel des Threads. Darum geht es hier doch. Ein Background-Thread wird jedoch - wenn bei bei Applikationsende noch läuft - hart abgebrochen, quasi so wie mit mit dem zu recht verpönten Thread.Abort bzw. noch härter. Wenn man also einen Thread sauber beenden will, sollte man nie Background-Threads verwenden.

herbivore

Midnight Run Themenstarter:in
80 Beiträge seit 2008
vor 12 Jahren

Hallo Freunde,
leider klappt es mit den Thread nicht so wie ich möchte, aber diesmal will ich alles sauber lösen.

Problem :
Diagnosetool, welches per Button Click ein Verzeichnis Überprüft, sprich es wird iteriert ob es bestimmte Dateien im Verzeichnis gibt. Dies soll sukzessiv angezeigt werden.

Also :

Datei 1 gefunden
Datei 2 nicht gefunden
etc ...

Danach wird noch ein Bild generiert, welches den Status Wiedergeben soll.

Dies geschieht alles in der Methode "Analyse". Nun hat der Entwickler vor mir, die komplette Methode in einem Foreground Thread starten lassen :


        private void btnStart_Click(object sender, EventArgs e)
        {
            tbOutput.Text = ""; 
            System.Threading.Thread analyseThread = new System.Threading.Thread(Analyse);
            analyseThread.Start(); 
        }

Das sukzessive Verhalten, wurde durch Invokes erreicht.

Nun ist es aber so, dass wenn man den Button zwei mal klickt der erste Thread noch weiterläuft oder wird das verhindert durch das neue erstellen des Threads:

System.Threading.Thread analyseThread = new System.Threading.Thread(Analyse);

???

Ein weiteres Problem, welches ich feststellte ist, dass wenn der Thread läuft und ich das Programm beende, dieser natürlich in eine Exception läuft, da die Form weg ist, aber der Invoke oder das Bild auf diese Zugreifen wollen.

Nun habe ich mir einige Artikel durchgelesen, aber komme der Lösung nicht nahe.

Ich würde denken, dass wenn man den Button zwei mal bspw. klickt, zuerst sichergestellt werden muss, dass der noch laufende beendet wurde.

Ich habe bei Stackoverflow den Artikel Is there a way to indefinitely pause a thread? gelesen und auch so umgesetzt, doch bei dem Thread.Join() hängt sich die Anwendung auf.

Ich habe es genauso umgesetzt, wie es Brannon beschreibt.

Auch bei schließen der Anwendung, also beim FormClosing Event, würde ich dieses prüfen.

Ich habe mich gestern ein wenig durch CodePlex Projekte gearbeitet um zu sehen, wie es andere lösen und fand ziemlich oft das Application.DoEvents() welches ich aber vermeiden will.

Habt ihr ein paar Lösungsansätze ?

Gruß

F
10.010 Beiträge seit 2004
vor 12 Jahren

Ja, versuch nicht irgendwas nachzufrickeln was andere machen, lerne die Doku zu lesen.
Was steht denn da bei Thread.Join?

  1. Wenn deine Anwendung die Funktion nicht mehrfach gleichzeitig aufrufen können soll, sperre den Button.

  2. Teste erstmal ob das abbrechen sauber funktioniert, dann beendest du erst den Thread und machst dann den Join, der wartet dann auf das beenden.

  3. Hör auf dir aus Forumseinträgen die Grundlagen erlesen zu wollen, nimm ein tutorial.

Midnight Run Themenstarter:in
80 Beiträge seit 2008
vor 12 Jahren

Hallo FZelle,
den Button zu deaktivieren, wäre der einfachste Workaround, würde aber nicht das Problem beim schließen der Anwendung beheben.

Ich habe bereits mich eingelesen bei dem Tutorial von Albahari zum Thema, aber so spezifisch geht er darauf nicht ein.

Kennst du den eins ? Mich hat es schon gewundert, dass bei vielen Open Source Projekten auch so gearbeitet wird.

Gruß

F
10.010 Beiträge seit 2004
vor 12 Jahren

Das ist das Problem i.a. mit OS Projekten, die Codequalität ist sehr häufig fürn a...

  1. Das ist kein Workaround, im gegenteil es ist eine gute Angewohnheit UI-Elemente zu sperren die der Benutzer nicht benutzen soll/darf.
    Also z.b. das sperren eines Buttons während die Aktion läuft.

  2. Es gibt nur 2 Möglichkeiten einen Thread zu beenden, cooperativ oder mit dem Hammer.
    Wenn du also keine Möglichkeit findest das sich der Thread relativ zügig durch setzen einer Variablen selber beendet, bleibt dir nichts anderes übrig als in zu aborten.

Multithreading in C#
Sah auf den ersten Blick nicht so schlecht aus, vor allem weil es mal die Grundlagen aufzeigt.

Midnight Run Themenstarter:in
80 Beiträge seit 2008
vor 12 Jahren

Danke für deine Hilfe. Ich werde es mir nun ansehen.

Gruß

49.485 Beiträge seit 2005
vor 12 Jahren

Hallo Midnight Run,

Nun ist es aber so, dass wenn man den Button zwei mal klickt der erste Thread noch weiterläuft

das sind Grundlagen, egal ob man das nun durch Disablen des Button löst, oder durch eine Abfrage, ob der Thread noch läuft, also [Hinweis] Wie poste ich richtig? Punkt 1.1.1.

Ein weiteres Problem, welches ich feststellte ist, dass wenn der Thread läuft und ich das Programm beende, dieser natürlich in eine Exception läuft, da die Form weg ist, aber der Invoke oder das Bild auf diese Zugreifen wollen.

Das ist ein Standard-Problem, auf das natürlich in der FAQ eingegangen wird. Siehe [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke). Außerdem gibt es mehrere Threads im Forum dazu, z.B. Threading in Fenstern: korrekte Vorgehensweise beim Schließen der Form. Also [Hinweis] Wie poste ich richtig? Punkt 1.1.

doch bei dem Thread.Join() hängt sich die Anwendung auf.

Natürlich, steht auch in der FAQ.

Application.DoEvents() welches ich aber vermeiden will.

Ein guter Vorsatz; steht auch so in der FAQ.

versuch nicht irgendwas nachzufrickeln was andere machen, lerne die Doku zu lesen. [...]Hör auf dir aus Forumseinträgen die Grundlagen erlesen zu wollen, nimm ein tutorial.

Dem kann man nur zustimmen. Siehe auch [FAQ] Wie finde ich den Einstieg in C#?

herbivore

Thema geschlossen