| |
VB.NET - Ein- und UmsteigerThread anhalten/fortsetzen und sicher beenden | | | Autor: keco | Datum: 11.09.08 12:14 |
| Hallo,
ich suche eine Möglichkeit einen Thread anzuhalten und später fortzusetzen. Ich habe zwei Buttons auf meiner GUI, einen zum Starten und einen zum Stoppen. Der Thread macht eigentlich nicht anderes als vorerst die aktuelle Zeit in eine Textbox zu schreiben.
Mir helfen allerdings keine Antworten die besagen ich solle einen Timer verwenden oder einen BGW ;) Ich übe gerade ein wenig mit Thread herum um mich ein wenig darin einzuarbeiten, deswegen.
Hier im Forum habe ich etwas gefunden vonwegen Sleep. Aber wenn ich auf den Button klicke und der Code
Threading.Thread.Sleep(Threading.TimeOut.Infinite) ausgeführt wird, dann habe ich damit den Hauptthread schlafen gelegt anstatt meines "TimeeThreads". Ist ja nicht mein Ziel. Und bei der Verwendung von .Suspend() bekomme ich eine Warnung, dass das veraltet sei und ich andere Klassen verwenden soll, wovon ich allerdings auch keine Ahnung habe.
Kennt jemand von Euch eine Lösung zum einfachen anhalten und fortsetzen des Threads? Eine Möglichkeit zum sicheren Beenden wäre auch ganz sinnvoll. Danke schonmal. | |
Re: Thread anhalten/fortsetzen und sicher beenden | | | Autor: keco | Datum: 11.09.08 14:24 |
| Danke erstmal. Ich habe das mit dem .Set() und .Reset() mal eingebaut, bin mir allerdings nicht sicher, ob das so vom Prinzip her richtig umgesetzt ist. Zur Hilfe habe ich das Beispiel in der MSDN genutzt, was unter Threading.ManualResetEvent steht:
Public Class frmInvoke
Dim manualEvent As Threading.ManualResetEvent
Private TimerThread As New Threading.Thread(New Threading.ThreadStart( _
AddressOf ThreadWork))
Private Delegate Sub TimeDelegate()
Private Sub frmInvoke_Load(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles MyBase.Load
manualEvent = New Threading.ManualResetEvent(False)
TimerThread.IsBackground = True
TimerThread.Start()
End Sub
Private Sub btnStartTimer_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles btnStartTimer.Click
manualEvent.Set()
End Sub
Private Sub btnStopTimer_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles btnStopTimer.Click
manualEvent.Reset()
End Sub
Public Sub ThreadWork()
Do
manualEvent.WaitOne()
Me.tbTime.Invoke(New TimeDelegate(AddressOf RefreshTime))
Threading.Thread.Sleep(200)
Loop
End Sub
Public Sub RefreshTime()
Me.tbTime.Text = Date.Now.ToLongTimeString & ":" & _
Date.Now.Millisecond.ToString
End Sub
End Class So funktioniert es zumindest.
Noch eine Frage zur Thematik: Der Thread ist im Moment auf Background gesetzt. Muss ich den Thread beim Schließen der Form dennoch beenden oder gilt das nur für Vordergrundthreads, oder muss ich das vom Primzip her überhaupt nicht beenden, wenn die Form geschlossen wird? | |
Re: Thread anhalten/fortsetzen und sicher beenden | | | Autor: keco | Datum: 11.09.08 14:34 |
| Okay, also vor dem Schließen der Form lieber prüfen ob der Thread beendet wurde, eventuell eine Warnung zeigen etc. Gut, ich Danke dir. | |
Re: Thread anhalten/fortsetzen und sicher beenden | | | Autor: keco | Datum: 11.09.08 14:42 |
| Da war ich etwas zu schnell mit meiner Antwort... Wie überprüfe ich denn überhaupt ob der Thread gerade läuft, also der Status von manualEvent auf signaled steht? Der ThreadState vom dem Thread steht immer auf "Background, WaitSleepJoin"...
Solange das manualEvent den Status signaled hat, soll eine Meldung erscheinen, die dann abfragt, ob beendet werden soll oder nich, ansonsten soll die Form gleich beendet werden. Wie kann das ich das überprüfen? | |
Re: Thread anhalten/fortsetzen und sicher beenden | | | Autor: DaveS (Moderator) | Datum: 11.09.08 15:28 |
| So sieht das ganze aus
Imports System.Threading
Public Class Form1
Dim t As New Thread(AddressOf doit)
Dim term As Boolean
Dim running As Boolean
Dim halt As New ManualResetEvent(True)
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles MyBase.Load
t.Start()
running = True
End Sub
' Stop thread
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles Button1.Click
Debug.WriteLine("Stopping thread")
halt.Reset()
running = False
End Sub
' Restart thread
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles Button2.Click
Debug.WriteLine("Restarting thread")
halt.Set()
running = True
End Sub
Private Sub doit()
While Not term
halt.WaitOne()
Debug.WriteLine("Thread running")
Thread.Sleep(500)
End While
Debug.WriteLine("Thread terminating")
End Sub
' End application
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles Button3.Click
Debug.WriteLine("Terminating")
term = True
t.Join()
Debug.WriteLine("Application terminating")
Me.Close()
End Sub
End Class Den Status vom MRE kannst du nur wissen, wenn du darüber Buch führst, hier mit 'running'. Du kannst natürlich auch Buttons enablen, disablen, Labels setzen oder was immer.
________
Alle Angaben ohne Gewähr. Keine Haftung für Vorschläge, Tipps oder sonstige Hilfe, falls es schiefgeht, nur Zeit verschwendet oder man sonst nicht zufrieden ist | |
Re: Thread anhalten/fortsetzen und sicher beenden | | | Autor: keco | Datum: 12.09.08 12:01 |
| Danke für den Code und deine Eklärung. In deinem Code musste ich aber etwas abändern bei dem Button3_Click. Wenn der Thread nicht läuft (also gestoppt wurde), dann wird t.Join ausgeführt und gewartet bis sich der Thread beendet, was aber nicht der Fall sein kann, da er nicht läuft. Deswegen habe ich eine Prüfung gemacht ob der Thread läuft, wenn ja, dann deinen Code ausführen, wenn nein, dann nur schließen.
Also das mit dem TimeOut verstehe ich irgendwie nicht, was du damit meinst. Meinst du damit, dass ich den Thread weiterlaufen lassen kann, obwohl halt.Reset() gesetzt wurde?
Also ich habe jetzt testweise halt.WaitOne(1000) gesetzt. Wenn ich nun Stoppe, dann bleibt der Thread nicht stehen, sondern führt nach den 1000 Millisekunden seine Arbeit weiter, ich denke das meintest du, oder? Jedenfalls hängt es dann beim Schließen der Form. Und zwar bei dem t.Join, das ist das letzte, was in der Form passiert.
Und mit der oben genannten Prüfung beim Schließen kann es sein, dass ich danach eine Exception bekomme, wenn in der doit ein Invoke ausgeführt wird. Wie würdest du das denn lösen mit dem Schließen der Form bei erwähnten Problemen? | |
Re: Thread anhalten/fortsetzen und sicher beenden | | | Autor: DaveS (Moderator) | Datum: 12.09.08 12:49 |
| Klar, man muss sicherstellen, dass der Thread sich beenden kann, das war nur schnell geschrieben als Beispiel. Einfach .Set() ausführen vor dem .Join() am Ende, zB. Mit Timeout kannst du einfach prüfen ob WaitOne() sperren würde. Hier ist ein Beispiel
' Thread routine
Private Sub doit()
While Not term
If Not halt.WaitOne(0, False) Then
' Hier kann der Thread etwas machen bevor er angehalten wird.
Debug.WriteLine("Now I'm going to stop, probably")
halt.WaitOne()
End If
Debug.WriteLine("Thread running")
Thread.Sleep(500)
End While
Debug.WriteLine("Thread terminating")
End Sub
' End application
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles Button3.Click
Debug.WriteLine("Terminating")
term = True
halt.Set() ' Ensure thread is running
t.Join()
Debug.WriteLine("Application terminating")
Me.Close()
End Sub Bis jetzt hatte ich keine Exceptions oder so. Bei Threads kann man schon eine Menge falsch machen, oft gibt es sporadische Probleme und Verhaltensstörungen und es ist oft schwer nachvollziehbar was passiert und warum die Dinge so passieren. Da zählt nur die Erfahrung
________
Alle Angaben ohne Gewähr. Keine Haftung für Vorschläge, Tipps oder sonstige Hilfe, falls es schiefgeht, nur Zeit verschwendet oder man sonst nicht zufrieden ist | |
Re: Thread anhalten/fortsetzen und sicher beenden | | | Autor: DaveS (Moderator) | Datum: 12.09.08 13:09 |
| Es fällt mir ein was du meinst. .Invoke() braucht den Hauptthread, sonst hängt der Thread am Aufruf. Da hilft eine Prüfung, dass die Anwendung noch läuft und eventuell DoEvents() bis der Thread fertig ist. Du kannst auch ein Event benutzen, was der Childthread postet um den Hauptthread zu benachrichtigen, dass er fertig ist. Manchmal können die Sachen ganz schön kompliziert werden. Ich habe ganze Klassen für Server-Anwendungen um Threads anzulegen, alle zu Stoppen, warten, bis sie fertig sind, im schlimmsten Fall abzuschiessen, Threads neuzustarten nach einem Fehler usw.
________
Alle Angaben ohne Gewähr. Keine Haftung für Vorschläge, Tipps oder sonstige Hilfe, falls es schiefgeht, nur Zeit verschwendet oder man sonst nicht zufrieden ist | |
Re: Thread anhalten/fortsetzen und sicher beenden | | | Autor: keco | Datum: 12.09.08 13:33 |
| Danke für deine Geduld und deine Hilfe! Mit dem jetzigen Code zeigt er mir im Direkfenster alles richtig an, beendet sich auch und läuft alles so, wie es soll. Allerdings gibts, wie du gerade geschrieben hast, Probleme mit dem Invoke().
Private Sub doit()
While Not term
If Not halt.WaitOne(0, False) Then
' Hier kann der Thread etwas machen bevor er angehalten wird.
Debug.WriteLine("Now I'm going to stop, probably")
halt.WaitOne()
End If
Debug.WriteLine("Thread running")
Me.tbTime.Invoke(New TimeDelegate(AddressOf RefreshTime))
Thread.Sleep(500)
End While
Debug.WriteLine("Thread terminating")
End Sub So funktioniert es an sich auch, allerdings nicht, wenn ich den Thread stoppe und dann das Fenster schließe. Ich bekomme keinen Fehler/Exception oder sonst etwas, nichts. Was ich angezeigt bekomme ist folgendes:
Thread running
Thread running
Thread running
Thread running
Thread running
Thread running
Stopping thread
Now I'm going to stop, probably
Terminating
Thread running Im Debugger bleibt die Zeile mit dem Invoke grau grundiert, dort bleibt alles stehen. Also müsste ich an der Stelle die Prüfung vornehmen? Wie mache ich die? Am Anfang, als ich einige Posts vorher die Exception erhielt, hätte ich wohl mit Me.IsDisposed überprüfen können.
Mit dem Event macht bei dem jetzigen Thread, den ich habe (aktuelle Zeit anzeigen) wenig Sinn, da er seine Arbeit ja nie erledigt hat. Aber das Event wäre dann in etwa so wie bei dem BGW und müsste ich theoretisch an der Stelle im doit nach der While auslösen?
Mit den Threads ist es wohl doch schwieriger als ich dachte...
Btw.: Ist das normal, dass ich im Direktfenster beim Beenden folgende Zeilen mehr als 6x angezeigt bekomme (habe das nicht gezählt)?:
Terminating
Application terminating
Terminating
Application terminating | |
Re: Thread anhalten/fortsetzen und sicher beenden | | | Autor: DaveS (Moderator) | Datum: 12.09.08 14:21 |
| Eine einfache Lösung wäre die Variable running zu verwenden, was der Thread zurücksetzt am Ende. Bei solchen Sachen muss aber immer auf race conditions aufpassen. Weil zwei Dinge "parallel" laufen, sogar physich auf Prozessoren mit mehreren Kernen, kann man nicht wissen genau was zuerst gemacht wird bzw fertig wird. ZB wichtig hier ist die Reihenfolge
running = True
term = True in Button3_Click(). Andersrum könnte der Thread term prüfen und beendet sein bevor running = true gesetzt wird, in welchem Fall, die Anwendung in der DoEvents Schleife hängebleiben würde.
Public Delegate Sub LogLineHandler(ByVal t As String)
Public Sub LogLine(ByVal t As String)
If Me.InvokeRequired Then
Me.Invoke(New LogLineHandler(AddressOf LogLine), New String() {t})
Return
End If
RichTextBox1.AppendText(t & vbCrLf)
RichTextBox1.ScrollToCaret()
End Sub
' Stop thread
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles Button1.Click
LogLine("Stopping thread")
halt.Reset()
running = False
End Sub
' Restart thread
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles Button2.Click
LogLine("Restarting thread")
running = True
halt.Set()
End Sub
' Thread routine
Private Sub doit()
While Not term
If Not halt.WaitOne(0, False) Then
LogLine("Now I'm going to stop, probably")
halt.WaitOne()
LogLine("Now I'm running (again)")
End If
LogLine("Thread running")
Thread.Sleep(500)
End While
LogLine("Thread terminating")
running = False
End Sub
' End application
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles Button3.Click
LogLine("Terminating")
running = True
term = True
halt.Set() ' Ensure thread is running
While running
Application.DoEvents()
Thread.Sleep(100)
End While
t.Join()
LogLine("Application terminating")
Me.Close()
End Sub ________
Alle Angaben ohne Gewähr. Keine Haftung für Vorschläge, Tipps oder sonstige Hilfe, falls es schiefgeht, nur Zeit verschwendet oder man sonst nicht zufrieden ist | |
Re: Thread anhalten/fortsetzen und sicher beenden | | | Autor: keco | Datum: 12.09.08 15:11 |
| So läuft das ganze prima, vielen Dank. Allerdings verstehe ich nich so ganz, wie die Funktion LogLine funktioniert, also mit dem Invoke. Du hast ja dort Me.Invoke() stehen, du beeinflusst doch aber das RichTextBox-Control? Ich hätte da RichTextBox1.Invoke() geschrieben und auf eine entsprechende Methode gezeigt mit einem Delegate. Könntest du das vielleicht noch kurz erklären? | |
Re: Thread anhalten/fortsetzen und sicher beenden | | | Autor: keco | Datum: 12.09.08 15:33 |
| Also könnte ich in der Methode auxh auf alle anderen Controls auf die gleiche einfache Weise zugreifen, wenn ich das so mache...
Ich danke nochmal für deine Geduld und Hilfe!
Mit den Threads ist wirklich eine schwere aber auch interessante Sache. Ich sollte mich weiterhin damit beschäftigen und in der Richtung lernen. | |
| Sie sind nicht angemeldet! Um auf diesen Beitrag zu antworten oder neue Beiträge schreiben zu können, müssen Sie sich zunächst anmelden.
Einloggen | Neu registrieren |
|
|
vb@rchiv CD Vol.6 vb@rchiv Vol.6
Geballtes Wissen aus mehr als 8 Jahren vb@rchiv!
Online-Update-Funktion Entwickler-Vollversionen u.v.m.Jetzt zugreifen Tipp des Monats Neu! sevCommand 4.0
Professionelle Schaltflächen im modernen Design!
Mit nur wenigen Mausklicks statten auch Sie Ihre Anwendungen ab sofort mit grafischen Schaltflächen im modernen Look & Feel aus (WinXP, Office, Vista oder auch Windows 8), inkl. große Symbolbibliothek. Weitere Infos
|