Threading mit ConcurrentBag (Producer-Consumer-Problem)

    • VB.NET
    • .NET (FX) 4.0

      Threading mit ConcurrentBag (Producer-Consumer-Problem)

      Das Problem
      Angenommen ein Netzwerk mit mehreren Geräten, die - wie es ihnen gefällt - gelegentlich Daten an ein Programm schicken, das das dann anzeigen soll.
      Erstes Problem: die Daten kommen - bei 3 Geräten - in 3 verschiedenen Threads an.
      Kein Problem, in WinForms transferiert man den Aufruf der Anzeige mittels Control.BeginInvoke in den Gui-Thread
      Problematischer, wenn die Geräte häufig senden, mw. jedes 10 mal pro Sekunde. Dann hätte man sekündlich ca. 30 Thread-Transfers, und so ein Transfer ist verhältnismäßig langsam, und vor allem die Anzeige 30 mal pro Sekunde zu ändern verursacht irrsinnig ProzessorLast 8|

      Lösung
      Ich schalte eine System.Collections.Concurrent.ConcurrentBag als Puffer zwischen die Producer und den Consumer.
      In diese Collection können die Producer beliebig Daten hineinschmeißen, die ist threadsicher.
      Und der Consumer im Gui-Thread kann gleichzeitig beliebig Daten entnehmen, ohne dass irgendwas einander in die Quere kommt.
      Und weil ich die Anzeige nicht 30 mal pro Sekunde updaten will, stelle ich einen Timer zum Daten-Konsum, mw. auf 300ms, das ist reichlich schnell genug für den Menschen, der davorsitzt.

      VB.NET-Quellcode

      1. Imports System.Collections.Concurrent
      2. Imports System.Threading
      3. Public Class Form1
      4. Private _Results As New ConcurrentBag(Of String)
      5. Private Const _Count As Integer = 20
      6. Private Sub A()
      7. For i = 0 To _Count
      8. _Results.Add("A: " & i)
      9. Thread.Sleep(300)
      10. Next
      11. End Sub
      12. Private Sub B(ByVal lst As ConcurrentBag(Of String))
      13. For i = 0 To _Count
      14. lst.Add("B: " & i)
      15. Thread.Sleep(400)
      16. Next
      17. End Sub
      18. Private Sub C(ByVal lst As ConcurrentBag(Of String), ByVal count As Integer)
      19. For i = 0 To count
      20. lst.Add("C: " & i)
      21. Thread.Sleep(500)
      22. Next
      23. End Sub
      24. Private Sub btStart_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btStart.Click
      25. 'starte 3 Producer-Threads
      26. With New Action(AddressOf A)
      27. .BeginInvoke(AddressOf .EndInvoke, Nothing)
      28. End With
      29. With New Action(Of ConcurrentBag(Of String))(AddressOf B)
      30. .BeginInvoke(_Results, AddressOf .EndInvoke, Nothing)
      31. End With
      32. With New Action(Of ConcurrentBag(Of String), Integer)(AddressOf C)
      33. .BeginInvoke(_Results, 21, AddressOf .EndInvoke, Nothing)
      34. End With
      35. End Sub
      36. Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As EventArgs) Handles Timer1.Tick
      37. If _Results.Count = 0 Then Return
      38. Dim s = ""
      39. ListBox1.BeginUpdate()
      40. While _Results.TryTake(s)
      41. ListBox1.Items.Add(s)
      42. End While
      43. ListBox1.EndUpdate()
      44. End Sub
      45. End Class
      Die Producer heissen bei mir A, B, C. Es sind Methoden mit unterschiedlichen Argumenten, die in unterschiedlichen Intervallen ihre Ergebnisse letztlich in dieselbe ConcurrentBag füllen.
      Im TimerTick wird die Collection ausgeräumt (aber nur, wenn was drin ist) und in die Listbox gefüllt.
      Returnt .TryTake() False, so ist die Collection leer und der Umfüll-Vorgang fertig für dieses Mal.

      Die Listbox zeigt schön, wie die Producer ganz unsortiert ihre Ergebnisse reingeschmissen haben, und man sieht auch, dass A zuerst fertig ist, dann B, zuletzt C


      Edit:
      Neben ConcurrentBag gibts noch weitere ConcurrentCollections: BlockingCollection, ConcurrentQueue, ConcurrentStack, ConcurrentDictionary - mit je spezifischem Verhalten.
      ConcurrentBag , ConcurrentQueue, ConcurrentStack implementieren übrigens das sog. IProducerConsumerCollection(Of T) - Interface.
      Dateien
      • MultiProducer.zip

        (16,11 kB, 150 mal heruntergeladen, zuletzt: )

      Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „ErfinderDesRades“ ()