Problem beim Zugriff auf Listen aus einem anderen Thread

  • VB.NET
  • .NET (FX) 3.0–3.5

Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von Daniel Schepers.

    Problem beim Zugriff auf Listen aus einem anderen Thread

    Guten Abend zusammen :)

    ich bin zwar schon relativ lange unter den Programmierern, stehe jedoch im Moment vor einem Problem bei dem ich nicht weiter weiß,
    da mir auch kein sinnvoller Suchbegriff für Google einfällt.

    Ich habe ein größeres Projekt in dem ich mit verschiedenen Threads arbeite. Um mein Problem einfacher darzustellen habe
    ich ein Projekt erstellt welches ziemlich simpel gehalten ist und doch mein Problem wiederspiegelt.

    Beim Laden der ersten Form1 erstelle ich eine Liste in einer anderen Klasse (Firmenverwaltung1). Wenn ich diese
    Liste direkt erstelle funktioniert alles wie es soll. Erstelle ich diese Liste indem ich einen separaten Thread starte
    kann ich zwar die Liste sehen, jedoch können dann angehängte Prozesse dort nicht richtig drauf zugreifen.

    Kann sich einer von euch mein Testprojekt mal ansehen?

    Der Unterschied zeigt sich wenn man bei dem Form1.load Sub entweder
    -Firmenverwaltung1.lade_Firmen <- Ohne neuen Thread
    oder
    Firmenverwaltung1.Starten <- Mit separaten Thread

    aufruft.

    Da ich beim Starten des eigentlichen Programmes eine Menge Daten lade wollte ich dies in einem separaten Thread machen.
    Danach läuft es aber nicht so wie es soll.

    Hier das ganze Projekt auch als Dropboxdownload: dropbox.com/sh/r1c5dmr39jyfl4c…TOSCs9qw4k1IIinoNQ0a?dl=0

    Quellcode

    1. Public Class Form1
    2. Public WithEvents Firmenverwaltung1 As New Firmenverwaltung
    3. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    4. 'Firmenverwaltung1.Start()
    5. Firmenverwaltung1.Lade_Firmen()
    6. End Sub
    7. Public Sub Firma_steuern(FirmenName As String, Wert As Integer)
    8. Dim counter As Integer = 0
    9. While counter < Firmenverwaltung1.Firmen.Count
    10. Dim Firma As Firmenverwaltung.Firma = Firmenverwaltung1.Firmen(counter)
    11. If Firma.Name = FirmenName Then
    12. Firma.Wert = Firma.Wert + Wert
    13. Firmenverwaltung1.Firmen(counter) = Firma
    14. End If
    15. counter = counter + 1
    16. End While
    17. End Sub
    18. Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    19. ListBox1.Items.Clear()
    20. Dim counter As Integer = 0
    21. While counter < Firmenverwaltung1.Firmen.Count
    22. Dim Firma As Firmenverwaltung.Firma = Firmenverwaltung1.Firmen(counter)
    23. ListBox1.Items.Add(Firma.Name + " - " + Firma.Wert.ToString)
    24. counter = counter + 1
    25. End While
    26. End Sub
    27. Private Sub NeueFirma(Firma As Firmenverwaltung.Firma) Handles Firmenverwaltung1.Neue_Firma
    28. Firmenverwaltung1.Firmen.Add(Firma)
    29. End Sub
    30. End Class
    31. Public Class Firmenverwaltung
    32. Public Structure Firma
    33. Dim Name As String
    34. Dim Wert As Integer
    35. Dim Prozessteuerung_thread As Prozesssteuerung
    36. End Structure
    37. Public Firmen As New List(Of Firma)
    38. Public Event Neue_Firma(Firma As Firma)
    39. Public Sub Start()
    40. Dim Ladethread As New Threading.Thread(AddressOf Lade_Firmen)
    41. Ladethread.IsBackground = False
    42. Ladethread.Start()
    43. End Sub
    44. Public Sub Lade_Firmen()
    45. Dim Firma1 As New Firma
    46. Firma1.Name = "Firma 1"
    47. Firma1.Wert = 0
    48. Firma1.Prozessteuerung_thread = New Prozesssteuerung
    49. Firma1.Prozessteuerung_thread.Firmenname = Firma1.Name
    50. Firma1.Prozessteuerung_thread.Add_Wert = 5
    51. AddHandler Firma1.Prozessteuerung_thread.Steuern, AddressOf Form1.Firma_steuern
    52. Firma1.Prozessteuerung_thread.Starten()
    53. RaiseEvent Neue_Firma(Firma1)
    54. Dim Firma2 As New Firma
    55. Firma2.Name = "Firma 2"
    56. Firma2.Wert = 0
    57. Firma2.Prozessteuerung_thread = New Prozesssteuerung
    58. Firma2.Prozessteuerung_thread.Firmenname = Firma2.Name
    59. Firma2.Prozessteuerung_thread.Add_Wert = 10
    60. AddHandler Firma2.Prozessteuerung_thread.Steuern, AddressOf Form1.Firma_steuern
    61. Firma2.Prozessteuerung_thread.Starten()
    62. Firmen.Add(Firma2)
    63. End Sub
    64. End Class
    65. Public Class Prozesssteuerung
    66. Public Firmenname As String = ""
    67. Public Add_Wert As Integer = 0
    68. Public Event Steuern(Firmenname As String, wert As Integer)
    69. Dim Steuerungsthread As New Threading.Thread(AddressOf Steuerungssub)
    70. Public Sub Steuerungssub()
    71. While True
    72. Threading.Thread.Sleep(3000) '10 Sekunden pause
    73. RaiseEvent Steuern(Firmenname, Add_Wert)
    74. End While
    75. End Sub
    76. Public Sub Starten()
    77. Steuerungsthread.IsBackground = True
    78. Steuerungsthread.Start()
    79. End Sub
    80. End Class
    @Daniel Schepers Willkommen im Forum. :thumbup:
    Die Suchbegriffe lauten: Invoke und BeginEnvoke.
    Wenn mehrere Threads gleichzeitig auf ein Control zugreifen, kann es natürlich zu Komplikationen kommen: Der eine sagt .Visible = True, der andere .Visible = False.
    Auch deswegen darf auf ein Control immer nur vom GUI-Thread aus zugegriffen werden.
    Du musst also den Ablauf in den GUI-Thread "invoken", das sieht z.B. so aus:

    VB.NET-Quellcode

    1. Sub DoIt(value As Integer)
    2. If Me.InvokeRequired Then ' Feststellen, ob invoked werden muss
    3. Dim ac = New Action(Of Integer)(DoIt) ' eine passende Action-Instanz erstellen
    4. Me.Invoke(ac, value) ' in den GUI-Thread invoken
    5. Return ' und raus !!!
    6. End If
    7. Me.NUD.Value = value ' das GUI-Control bedienen
    8. End Sub
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Besten Dank fürs Willkommen heißen und die Antwort heute morgen :)

    ich habe nun an zwei stellen versucht damit mein Problem zu lösen, einmal beim Firmen laden und einmal beim Werte ändern.
    Jedoch beides ohne Erfolg... Beim Debuggen fällt auf, das die If Abfrage Me.Invokerequired in dem Firma_Steuern Sub jedes mal false ausfällt...

    VB.NET-Quellcode

    1. Public Sub Firma_steuern(Wert As Integer, FirmenName As String)
    2. If Me.InvokeRequired Then ' Feststellen, ob invoked werden muss
    3. Dim ac = New Action(Of Integer, String)(AddressOf Firma_steuern) ' eine passende Action-Instanz erstellen
    4. Me.Invoke(ac, Wert, FirmenName) ' in den GUI-Thread invoken
    5. Return ' und raus !!!
    6. End If
    7. Dim counter As Integer = 0
    8. While counter < Firmenverwaltung1.Firmen.Count
    9. Dim Firma As Firmenverwaltung.Firma = Firmenverwaltung1.Firmen(counter)
    10. If Firma.Name = FirmenName Then
    11. Firma.Wert = Firma.Wert + Wert
    12. Firmenverwaltung1.Firmen(counter) = Firma
    13. End If
    14. counter = counter + 1
    15. End While
    16. End Sub
    17. Private Sub NeueFirma(Firma As Firmenverwaltung.Firma) Handles Firmenverwaltung1.Neue_Firma
    18. If Me.InvokeRequired Then ' Feststellen, ob invoked werden muss
    19. Dim ac = New Action(Of Firmenverwaltung.Firma)(AddressOf NeueFirma) ' eine passende Action-Instanz erstellen
    20. Me.Invoke(ac, Firma) ' in den GUI-Thread invoken
    21. Return ' und raus !!!
    22. End If
    23. Firmenverwaltung1.Firmen.Add(Firma)
    24. End Sub

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

    Daniel Schepers schrieb:

    jedes mal false ausfällt...
    bedeutet, dass der Fehler nicht an dieser Stelle auftritt.
    Poste mal die Zeile, in der die Exception auftritt.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    es fällt überhaupt keine exception... ich hab auch keine fehler abgefangen.
    in dem Sub "Firma_steuern" ist der Wert von Firmenverwaltung1.Firmen.Count gleich 0 obwohl ich am Programmstart 2 Elemente
    hinzufüge und die in dem Timer-Sub auch angezeigt werden können...

    Wenn ich die Firmen nicht in einem separaten Thread laden lasse sondern direkt bei Form1_load funktioniert es wohl...
    Definiere

    Daniel Schepers schrieb:

    Jedoch beides ohne Erfolg
    Hänge das bereinigte Projekt ohne obj- und bin-Verzeichnis an und schfreib, was wir tun müssen, um Deinen Effekt zu reproduzieren.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Es ist wie befürchtet:

    VB.NET-Quellcode

    1. AddHandler Firma1.Prozessteuerung_thread.Steuern, AddressOf Form1.Firma_steuern

    das funktioniert nunmal nicht, siehe Form und Threads.
    Kurz: Du kannst das nicht so machen, weil Form1 im (Neben-)Thread was anderes als im Hauptthread ist. Isso.

    Da muss auch nix invoked werden, da für das Nebenthread-Objekt Form1 nix Ungewöhnliches passiert, da alles threadintern ist.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    N Lösungsvorschlag meinerseits hätte meinem Beitrag noch gutgetan.
    Anstatt in der Firmenklasse das Event per AddHandler der vermeintlichen Form1-Sub zuzuordnen, wäre es m.E. korrekt, im Formular selbst das Ereignis der Firmenklasse zu abonnieren. So wird klar, dass das aufrufende Form1-Objekt quasi an sich adressiert. Mit Me wird da schon gearbeitet. Aber eben innerhalb von Form1.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Also es scheint zumindest zu funktionieren.

    Ich übergebe beim Laden dem neuen Thread die aktuelle Form:

    VB.NET-Quellcode

    1. firmenverwaltung1.Start(Me)



    VB.NET-Quellcode

    1. Public Sub Start(Programm1 As Form1)
    2. Dim Ladethread As New Threading.Thread(AddressOf Lade_Firmen)
    3. Ladethread.IsBackground = False
    4. Ladethread.Start(Programm1)
    5. End Sub



    VB.NET-Quellcode

    1. Public Sub Lade_Firmen(Programm1 As Form1)
    2. Threading.Thread.Sleep(1500)
    3. Dim Firma1 As New Firma
    4. Firma1.Name = "Firma 1"
    5. Firma1.Wert = 0
    6. Firma1.Prozessteuerung_thread = New Prozesssteuerung
    7. Firma1.Prozessteuerung_thread.Firmenname = Firma1.Name
    8. Firma1.Prozessteuerung_thread.Add_Wert = 5
    9. AddHandler Firma1.Prozessteuerung_thread.Steuern, AddressOf Programm1.Firma_steuern
    10. Firma1.Prozessteuerung_thread.Starten()
    11. Programm1.Firmenverwaltung1.Firmen.Add(Firma1)
    12. 'RaiseEvent Neue_Firma(Firma1)
    13. Dim Firma2 As New Firma
    14. Firma2.Name = "Firma 2"
    15. Firma2.Wert = 0
    16. Firma2.Prozessteuerung_thread = New Prozesssteuerung
    17. Firma2.Prozessteuerung_thread.Firmenname = Firma2.Name
    18. Firma2.Prozessteuerung_thread.Add_Wert = 10
    19. AddHandler Firma2.Prozessteuerung_thread.Steuern, AddressOf Programm1.Firma_steuern
    20. Firma2.Prozessteuerung_thread.Starten()
    21. ' RaiseEvent Neue_Firma(Firma2)
    22. Programm1.Firmenverwaltung1.Firmen.Add(Firma2)
    23. Firmen_geladen = True
    24. End Sub