Ende aller Threads

  • VB.NET

Es gibt 24 Antworten in diesem Thema. Der letzte Beitrag () ist von Kraizy.

    Naja eine Endlosschleife ist da ja nicht eingebaut..
    erst einmal die Frage was willst Du beenden und warum überhaupt ? Willst Du alle Threads beenden ( aus irgendwelchen Gründen) so wirst Du dir alle gestarteten Threads irgendwo ( z.B. einer Liste merken müssen) um sie später auchs stopnne zu können. Momentan merkst Du Dir in der Variable _thread nur den letzten ...

    Naja, der Benutzer fügt in eine ListBox/View seine Daten ein (diese werden dann in _list gespeichert) und drückt auf den Start-Button. Dann werden immer 5 Einträge daraus gleichzeitig genommen und z.B. als Parameter für die "Login"-Funktion aus der DLL übergeben. Und das ganze, wie bereits gesagt, eben so lange, bis alle Einträge durch sind. Der Benutzer soll jedoch die Möglichkeit haben, diesen Prozess zwischenzeitlich stoppen zu können, sodass er nicht unbedingt warten muss, bis alle Einträge komplett durch sind. Deshalb das Abort(), was aber wohl keine gute Lösung war..deswegen würde ich nun gerne wissen, wie ich das anders machen soll. Ein kleines Beispiel würde mir da ziemlich helfen..

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Kraizy“ ()

    Da Du anscheinend schon eine externe API für den Webrequest verwendest, wir der Vorschlag für einen asynchronen WR wohl eher unrealistisch sein.

    Kraizy schrieb:

    Der Benutzer soll jedoch die Möglichkeit haben, diesen Prozess zwischenzeitlich stoppen zu können, sodass er nicht unbedingt warten muss, bis alle Einträge komplett durch sind. Deshalb das Abort(), was aber wohl keine gute Lösung war..deswegen würde ich nun gerne wissen, wie ich das anders machen soll.

    Für einen harten Abbruch kannst Du ohne weiteres Abort verwenden, nur solltest Du die auftretende Exception dann auch sauber abfangen. Nur sollten dann alle laufenden Threads und nicht nur der letzte gestoppt werden.

    Das folgende zusammegehackte Beispiel wird bestimmt keinen Beauty Contest gewinnen, aber vielleicht bekommst Du ja ein paar Ideen wie Du das verbessern könntest. Es können durchaus noch Fehler enthalten sein:

    Beispielcode

    VB.NET-Quellcode

    1. Imports System.Threading
    2. Public Class Form1
    3. Dim WithEvents sendRequests As New sendRequests
    4. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    5. sendRequests.send(New List(Of String)({"blubb1", "blubb2", "blubb3", "blubb4", "blubb5", "blubb6", "blubb7", "blubb8"}))
    6. End Sub
    7. Public Sub complete() Handles sendRequests.SendCompleted
    8. MessageBox.Show("done")
    9. End Sub
    10. Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
    11. sendRequests.Stop()
    12. End Sub
    13. End Class
    14. ' class sendRequests
    15. Public Class sendRequests
    16. ' Public properties
    17. Const maxThreadCount = 5 ' Anzahl erlaubter paralleler Prozesse ( kann natürlich geändert werden)
    18. Public Event SendCompleted() ' wird ausgelöst wenn die Verarbeitung aller strings fertig ist
    19. ' private
    20. Private runningThreads As New List(Of Threading.Thread) ' liste der aktuell laufenden threads
    21. Private workItems As New List(Of String) ' liste der strings die noch zur Verarbeitung anstehen
    22. Private Event threadCompleted(ByVal t As Thread, ByVal s As String, ByVal success As Boolean) ' event beim normalen Ende eines Thread
    23. ' die übergebene Liste senden
    24. Public Sub send(ByVal requests As List(Of String))
    25. workItems.Clear()
    26. runningThreads.Clear()
    27. ' copy string to work list
    28. workItems.AddRange(requests.ToArray)
    29. startThreads()
    30. End Sub
    31. ' stop alle laufenden Threads
    32. Public Sub [Stop]()
    33. SyncLock runningThreads
    34. workItems.Clear()
    35. For Each t As Thread In runningThreads
    36. If (t IsNot Nothing AndAlso t.IsAlive) Then t.Abort()
    37. Next
    38. runningThreads.Clear()
    39. End SyncLock
    40. End Sub
    41. ' startet weitere Threads wenn noch strings vorliegen
    42. Private Sub startThreads()
    43. ' start new threads
    44. While runningThreads.Count < maxThreadCount AndAlso workItems.Count > 0
    45. ' neues item aus der Liste nehmen und austragen
    46. Dim item As String = workItems(0)
    47. workItems.Remove(item)
    48. ' thread mit workitem starten und als laufend eintragen
    49. Dim _t As Thread = New Thread(New ParameterizedThreadStart(AddressOf doWork))
    50. _t.IsBackground = True
    51. runningThreads.Add(_t)
    52. _t.Start(item)
    53. End While
    54. End Sub
    55. ' Verarbeitungs Thread
    56. Private Sub doWork(ByVal o As Object)
    57. Dim sendString As String = o.ToString
    58. Debug.Print("thread started: " & sendString)
    59. Dim result As Boolean
    60. Try
    61. ' hier sollte die Verarbeitung erfolgen ..... result is true or false
    62. Thread.Sleep(500) ' test only
    63. Catch ex As ThreadAbortException
    64. Debug.Print("thread aborted: " & sendString)
    65. End Try
    66. ' thread has completed successful/failed
    67. RaiseEvent threadCompleted(Thread.CurrentThread, sendString, result)
    68. End Sub
    69. ' Event: wird aufgerufen wenn ein Thread normal endet (nicht bei Abort):
    70. Private Sub threadCompletedHandler(ByVal t As Thread, ByVal s As String, ByVal result As Boolean) Handles Me.threadCompleted
    71. Debug.Print("thread has ended: " & s)
    72. ' aus Liste laufender Threads austragen
    73. SyncLock runningThreads
    74. runningThreads.Remove(t)
    75. End SyncLock
    76. ' restart new threads
    77. If workItems.Count > 0 Then startThreads() : Return
    78. ' check finished
    79. If runningThreads.Count = 0 Then RaiseEvent SendCompleted()
    80. End Sub
    81. End Class



    Der Code macht folgendes:
    - speichern der laufenden Threads in einer Liste
    - an jedem Ende eines Threads wird ein lokaler Event ausgelöst, in dem dann ggf. neue Threads gestartet werden
    - wenn alle Strings (erfolgreich oder failed) verarbeitet wurden, wird ein Public Event SendCompleted ausgelöst

    Die Debug.Print Statements sollen nur helfen die Abfolge zu verstehen, genauso ist das Sleep Statement in dem Verarbeitungsthread nur für den Test vorhanden.

    Kommentier ruhig einmal die Synclocks aus um zu verstehen was dann passieren kann.
    Bei Fragen melde Dich wieder ...

    P.S: auch wenn es bestimmt nett gemeint war, ich denke 1x Bedanken pro Thread bei der gleichen person reicht locker aus

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Kangaroo“ ()

    Ich nochmal, ist zwar schon etwas her, aber naja..
    Also dein Beispiel klappt an sich eigentlich ziemlich gut und ist auch genau das was ich gebraucht habe, jedoch funktioniert es leider nicht richtig mit der API..
    Das Problem ist, dass wenn ich z.B. 5 Threads gleichzeitig starte, dann wird die Login-Funktion aufgerufen, wenn der Login geklappt hat, werden einige weitere Funktionen aufgerufen, z.B. um ein Video hochzuladen oder sonst was..das Problem ist nun, dass nun alle 5 Threads auf die gleiche Variable in der Funktion zugreifen, zur Verdeutlichung:

    VB.NET-Quellcode

    1. 'Variablen
    2. Public YouTubeRequestSettings As YouTubeRequestSettings
    3. Public YouTubeRequest As YouTubeRequest
    4. Public YouTubeVideo As Video
    5. 'Login-Funktion (teilweise abgekürzt):
    6. Public Function Login(username As String, password As String) As Boolean
    7. Try
    8. YouTubeRequestSettings = New YouTubeRequestSettings(username, password)
    9. YouTubeRequest = New YouTubeRequest(YouTubeRequestSettings)
    10. YouTubeVideo = YouTubeRequest.Retrieve(Of Video)
    11. Debug.Print(String.Format("Login mit Account '{0}' erfolgreich.", username))
    12. Catch ex As Exception
    13. Debug.Print(String.Format("Login mit Account '{0}' fehlgeschlagen.", username))
    14. Return False
    15. End Try
    16. Return True
    17. End Function
    18. 'Andere Funktion:
    19. Public Function MachWas(username As String) As Boolean
    20. Try
    21. YouTubeVideo.MachWas(...) 'hier wird also auf die Variable YouTubeVideo zugegriffen, welches auch Daten von den RequestSettings enthält (somit auch username & password)
    22. Debug.Print(String.Format("Funktion mit Account '{0}' erfolgreich.", username))
    23. Catch ex As Exception
    24. Debug.Print(String.Format("Funktion mit Account '{0}' fehlgeschlagen.", username))
    25. Return False
    26. End Try
    27. Return True
    28. End Sub
    29. 'Aufrufen (mit deiner (Kangaroo) Methode (in DoWork)):
    30. ...
    31. If Login(username, password) Then
    32. MachWas(username)
    33. End If
    34. ...

    Das Problem ist nun, dass z.B. Thread 1 einen erfolgreichen Login hatte und somit nun MachWas aufruft, aber in der Zwischenzeit schon Thread 2 versucht hat sich mit den anderen Account einzuloggen und hat somit die RequestSettings-Variablen verändert, welche jedoch aber in der Funktion MachWas von Thread 1 benutzt werden. Somit wird entweder mit einem falschen Account etwas gemacht, oder gar mit einem ungültigen Account, was dann zu einer Fehlermeldung führt..

    Hoffe du verstehst was mein Problem ist..