vb@rchiv
VB Classic
VB.NET
ADO.NET
VBA
C#
NEU! sevCoolbar 3.0 - Professionelle Toolbars im modernen Design!  
 vb@rchiv Quick-Search: Suche startenErweiterte Suche starten   Impressum  | Datenschutz  | vb@rchiv CD Vol.6  | Shop Copyright ©2000-2024
 
zurück

 Sie sind aktuell nicht angemeldet.Funktionen: Einloggen  |  Neu registrieren  |  Suchen

VB.NET - Ein- und Umsteiger
Thread 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.
Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

Re: Thread anhalten/fortsetzen und sicher beenden 
Autor: ModeratorDaveS (Moderator)
Datum: 11.09.08 13:45

Für solche Sachen verwendet man die Synchronisierungsobjekte wie AutoResetEvent und ManualResetEvent. Vom ersten gibt's hier ein Beispiel wie man einen Thread mit Arbeit füttert (nicht ganz dein Thema, ich weiss). Beim zweiten setzt das Mainline das Objekt auf non-signalled (.Reset()) wenn der Thread anhalten soll, und auf signalled (.Set()) um den Thread wieder laufen zu lassen. Der Thread 'wartet' regelmässig aufs Event. Um einen Thread zu beenden braucht man nur eine Variable (Boolean) welche der Thread regelmässig prüft, und sich beendet wenn gesetzt. Mainline kann .Join() benutzen um auf Threadende zu warten. Es gibt auch raffiniertere Methoden wenn man mehrere Threads hat und das Mainline prüfen will ob alle Threads sich schon ordentlich beendet haben.

________
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

Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

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?
Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

Re: Thread anhalten/fortsetzen und sicher beenden 
Autor: ModeratorDaveS (Moderator)
Datum: 11.09.08 14:30

Background Threads werden automatisch beendet wenn der Hauptthread terminert. Das ist aber nicht gerade eine schöne Art mit Threads umzugehen. Ob das dann stört hängt davon ab ob der Thread wichtige Aufgaben erledigt und Daten vielleicht beschädigen könnte wenn nicht richtig beendet.

________
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

Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

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.
Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

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?
Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

Re: Thread anhalten/fortsetzen und sicher beenden 
Autor: ModeratorDaveS (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

Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

Re: Thread anhalten/fortsetzen und sicher beenden 
Autor: ModeratorDaveS (Moderator)
Datum: 11.09.08 17:31

Du kannst übrigens ein Timeout bei .WaitOne() angeben. Damit kannst du auch einfach prüfen ob das Event signalisiert ist oder nicht. Da es ein ManualResetEvent ist ginge das auch im Hauptthread. Du kannst zB im Thread so prüfen ob Anhalten signalisiert wurde, ohne eigentlich aufgehalten zu werden, was eventuell nützlich sein könnte.

________
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

Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

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?
Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

Re: Thread anhalten/fortsetzen und sicher beenden 
Autor: ModeratorDaveS (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

Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

Re: Thread anhalten/fortsetzen und sicher beenden 
Autor: ModeratorDaveS (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

Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

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
Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

Re: Thread anhalten/fortsetzen und sicher beenden 
Autor: ModeratorDaveS (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

Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

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?
Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

Re: Thread anhalten/fortsetzen und sicher beenden 
Autor: ModeratorDaveS (Moderator)
Datum: 12.09.08 15:22

Alle Controls laufen im gleichen Thread, daher ist es egal welches man verwendet. Ich nehme grundsätzliche die Form. Die LogFile() Routine ruft sich selbst rekursiv auf, wenn festgestellt wird (.InvokeRequired), dass der Aufruf aus einem fremden Thread kommt. Dann ist die ganze Logik zusammen an einem Ort. Man kann es aber ganz anders machen.

Wenn man einen Thread hat in einer GUI sollte man übrigens sehr sparsam sein mit .Invoke(), weil das immer den Thread serialisiert und der Thread aufgehalten wird bis .Invoke() irgendwann zurückommt. Das kann auch dazu führen, dass Hauptthread und Childthread sich gegenseitig sperren (deadly embrace, noch etwas worauf man beim Multithreading immer aufpassen muss). Wenn der Hauptthread irgendwo auf Ergebnisse vom Childthread wartet und der Childthread .Invoke() verwendet kann es ganz leicht passieren.

________
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

Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

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.
Themenbaum einblendenGesamtübersicht  |  Zum Thema  |  Suchen

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

Funktionen:  Zum Thema  |  GesamtübersichtSuchen 

nach obenzurück
 
   

Copyright ©2000-2024 vb@rchiv Dieter Otter
Alle Rechte vorbehalten.
Microsoft, Windows und Visual Basic sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern. Weitere auf dieser Homepage aufgeführten Produkt- und Firmennamen können geschützte Marken ihrer jeweiligen Inhaber sein.

Diese Seiten wurden optimiert für eine Bildschirmauflösung von mind. 1280x1024 Pixel