Thread beenden, Thread.Abort funktioniert nicht?

  • VB.NET

Es gibt 4 Antworten in diesem Thema. Der letzte Beitrag () ist von bussibaer.

    Thread beenden, Thread.Abort funktioniert nicht?

    Hallo,

    ich experimentiere gerade mit Threads und dem beenden eines laufenden Threades. Dazu habe ich mir aus dem Galileo Online Handbuch aus Kapitel 18.3.5 das Beispiel FileWatchService rauskopiert und in eine Konsolenanwendung abgeändert. Da in VB2008 Thread.Resume und Thread.suspend als veraltet genannt werden habe ich versucht ohne diese beiden auszukommen und ganz simpel Thread.Start und Thread.Abort zu benutzen.

    Jetzt habe ich aber festgestellt, das der Thread trotz Thread.Abort nicht beendet wird. Woran kann das liegen?

    Hier mal ein Auszug meines Codes:

    Hier das Hauptmodul

    VB.NET-Quellcode

    1. Imports System.Threading
    2. Module Start
    3. 'Event zum Beenden
    4. Public Event Finished()
    5. Private th As Thread
    6. Private SavArgs As String
    7. Private path As String = "f:\\"
    8. Sub Main()
    9. Dim Auswahl As String = ""
    10. MyScreenWrite()
    11. Do Until Auswahl = "x"
    12. Auswahl = MyScreenRead()
    13. Select Case Auswahl
    14. Case "1"
    15. Dim w As New worker(path)
    16. th = New Thread(New ThreadStart(AddressOf w.StartWorker))
    17. th.Start()
    18. Auswahl = ""
    19. Case "2"
    20. RaiseEvent Finished()
    21. th.Abort()
    22. th = Nothing
    23. Auswahl = ""
    24. Case "x"
    25. Case Else
    26. Console.WriteLine("Ungültige Auswahl -> " & Auswahl)
    27. End Select
    28. Loop
    29. End Sub
    30. Sub MyScreenWrite()
    31. Console.WriteLine("Bitte eine Auswahl treffen")
    32. Console.WriteLine("1 - Starten der Protokollierung")
    33. Console.WriteLine("2 - Beenden der Protokollierung")
    34. Console.WriteLine("x - Ende der Anwendung")
    35. End Sub
    36. Function MyScreenRead() As String
    37. MyScreenRead = Console.ReadLine()
    38. End Function
    39. End Module


    Hier der Thread

    VB.NET-Quellcode

    1. Imports System.Threading
    2. Public Class worker
    3. Private DoRun As Boolean = True
    4. Private protocolFile As String = IO.Path.Combine( _
    5. IO.Path.GetDirectoryName( _
    6. Process.GetCurrentProcess().MainModule.FileName), "FileWatchLog.log")
    7. Private path As String = "f:\zDonwloads\"
    8. Public Sub New(ByVal path As String)
    9. If Not String.IsNullOrEmpty(path) Then Me.path = path
    10. End Sub
    11. ' Startmethode des Threads
    12. Public Sub StartWorker()
    13. Dim fsw As New IO.FileSystemWatcher(Me.path)
    14. fsw.IncludeSubdirectories = True
    15. fsw.EnableRaisingEvents = True
    16. 'Dim th As ThreadAbortException
    17. AddHandler fsw.Changed, AddressOf ChangeFile (Wegen Übersichtlichkeit gelöscht)
    18. AddHandler fsw.Deleted, AddressOf DeleteFile (Wegen Übersichtlichkeit gelöscht)
    19. AddHandler fsw.Created, AddressOf CreateFile (Wegen Übersichtlichkeit gelöscht)
    20. AddHandler fsw.Renamed, AddressOf RenameFile (Wegen Übersichtlichkeit gelöscht)
    21. AddHandler Start.Finished, AddressOf EndThread
    22. Try
    23. ' in der Schleife wird auf etwaige Änderungen im Dateisystem gewartet
    24. While DoRun = True
    25. If DoRun = True Then
    26. fsw.WaitForChanged(IO.WatcherChangeTypes.All)
    27. Else
    28. fsw.EnableRaisingEvents = False
    29. End If
    30. End While
    31. Catch : End Try
    32. fsw.EnableRaisingEvents = False
    33. End Sub
    34. .
    35. .
    36. .
    37. .
    38. ' Soll diesen Thread beenden
    39. Private Sub EndThread()
    40. DoRun = False
    41. End Sub
    42. End Class
    Schönen Gruß aus Kiel

    Jörg

    bussibaer schrieb:

    Jetzt habe ich aber festgestellt, das der Thread trotz Thread.Abort nicht beendet wird. Woran kann das liegen?


    Bevor ich Dir Deine Frage beantworte, erst einmal etwas Basiswissen: für das Stoppen eines Threads gibt es 2 klassische Methoden
    a) setzen einer Booleschen Variable die in einer Schleife abgefragt wird, bei Dir entspricht es DoRun
    b) beenden des Threads durch die Thread.Abort Methode

    Die Variante a) hat den Vorteil, dass kontrolliert an einer bestimmten Stelle abgebrochen wird, wo die Logik es gerade vorsieht. In Deinem Fall ist das evtl nicht besonders sinnvoll, da der Call fsw.WaitForChanged blockiert bis tatsächlich ein FSW Event ausgelöst wird. Das heisst, Dein Thread wird erst beendet wenn die nächste Änderung auftritt.

    Die Variante b) löst eine ThreadAbort Exception im Thread aus. Ist kein Try..Catch Block gesetzt, so wird sofort abgebrochen, anderenfalls wird der Exceptionhandler aufgerufen. Nachteil ist hier allerdings, dass der Abbruch irgendwo auftreten kann, d.h. vielleicht irgendwo wo Aufgaben nur halb erledigt wurden.

    Jetzt zu Deiner Frage: Du löst den Abbruch durch Thread.Abort aus, im Thread wird der Catch Block ausgelöst(der nix macht) und der Thread bricht ab. Dummerweise steht immer noch fsw.EnableRaisingEvents = Trueund Dein FSW feuert unbeeinflusst im Hintergrund weiter seine Events. Dein Versuch irgendwie noch mit der Variable DoRun etwas zu machen greifen ins Leere, dumm gelaufen ... :rolleyes:

    Die einzig sinnvolle Lösung ist es im Catch Block die ThreadAbortException abzufangen, EnableEvents=False zu setzen und den FSW zu disposen.

    Ein paar Bemerkungen mehr:
    - bei Dir gibt es die Möglichkeit durch Eingabe von '1' mehrfache Threads zu starten, die Du nie abbrechen kannst und sogar nach Beendigung Deines Programms weiterlaufen (!)
    - entweder Variante a) oder b), nicht so ein Wuselcode mit dem Versuch beide zu kombinieren
    - das Beispiel im Galileo Buch ist ausnahmsweise Blödsinn: entweder eine Schleife mit der Abfrage fsw.WaitForChanged oder EnableEvents=True, beides zusammen ist doppelt gemoppelt
    - setze immer die Thread Property isBackground=True, so werden laufende Threads wenigstens bei Beenden Deines Programmes abgeräumt
    - zum Experimentieren nimm Dir lieber einfache Beispiele um Besonderheiten rauszuarbeiten
    @Kangaroo

    danke für deine Hilfe :thumbsup: . Das hat jetzt funktioniert:

    VB.NET-Quellcode

    1. Try
    2. ' in der Schleife wird auf etwaige Änderungen im Dateisystem gewartet
    3. While True
    4. fsw.WaitForChanged(IO.WatcherChangeTypes.All)
    5. End While
    6. Catch ex As ThreadAbortException ' ThreadAbortException duch th.abort ausgelöst
    7. fsw.EnableRaisingEvents = False
    8. fsw.Dispose()
    9. fsw = Nothing
    10. End Try


    Wie würdest du die While Schleife machen? Jetzt ist das ja eine Endlosschleife?
    Schönen Gruß aus Kiel

    Jörg

    bussibaer schrieb:

    Wie würdest du die While Schleife machen? Jetzt ist das ja eine Endlosschleife?

    Wie ich oben schon geschrieben hatte , machst Du entweder einen neuen Thread mit einer While Schleife oder Du definierst einen FSW mit Events und Eventhandler Routinen. Beides zusammen ist absolut sinnlos.

    Um das zu illustrieren hier ein simples Beispiel mit einem eigenen Thread und einer While Schleife mit WaitForChanged:

    VB.NET-Quellcode

    1. Option Strict On
    2. Imports System.Threading
    3. Public Class Form1
    4. Private watcherThread As Thread ' out watcher thread
    5. ' Toggle Button
    6. Private Sub ButtonToggle_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonToggle.Click
    7. If watcherThread IsNot Nothing AndAlso watcherThread.IsAlive Then
    8. ' stop thread
    9. watcherThread.Abort()
    10. watcherThread = Nothing
    11. Else
    12. ' start thread
    13. watcherThread = New Thread(Sub() watchFS()) With {.IsBackground = True, .Name = "watcherThread"}
    14. watcherThread.Start()
    15. End If
    16. End Sub
    17. Sub watchFS()
    18. ' define fsw
    19. Dim fsw As New IO.FileSystemWatcher("d:\download")
    20. Trace.WriteLine("Thread was started: " & Thread.CurrentThread.Name)
    21. Try
    22. While True
    23. ' wait for change
    24. Trace.WriteLine("waiting for new change event")
    25. Dim changeResult As IO.WaitForChangedResult = fsw.WaitForChanged(IO.WatcherChangeTypes.All)
    26. ' analyse change
    27. Select Case changeResult.ChangeType
    28. Case IO.WatcherChangeTypes.Created : Trace.WriteLine(String.Format("File or folder '{0}' was created", changeResult.Name))
    29. Case IO.WatcherChangeTypes.Changed : Trace.WriteLine(String.Format("File or folder '{0}' was changed", changeResult.Name))
    30. Case IO.WatcherChangeTypes.Renamed : Trace.WriteLine(String.Format("File or folder '{0}' was renamed to'{1}'", changeResult.OldName, changeResult.Name))
    31. Case IO.WatcherChangeTypes.Deleted : Trace.WriteLine(String.Format("File or folder '{0}' was deleted", changeResult.Name))
    32. End Select
    33. End While
    34. Catch ex As Exception
    35. Trace.WriteLine("Thread was aborted: " & Thread.CurrentThread.Name)
    36. fsw.Dispose()
    37. End Try
    38. End Sub
    39. End Class