Zur Laufzeit erzeugte Controls in einem anderen Thread ansprechen

  • VB.NET

Es gibt 41 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Es scheint so, dass ich zu blöd dafür bin.
    Ich verstehe es immernoch nicht ?(
    Ich sehe keinen Zusammenhang zu meinem Projekt

    VB.NET-Quellcode

    1. Public Sub SPS_Inputs()
    2. Dim a = 5
    3. Dim b = 6
    4. Dim c = 12
    5. Me.Invoke(Sub() MyInvokingSub(a, b, c))
    6. End Sub
    7. Private Sub MyInvokingSub(a As Integer, b As Integer, c As Integer)
    8. MessageBox.Show((a + b + c).ToString)
    9. End Sub


    VB.NET-Quellcode

    1. Public Sub SPS_Connection_Inputs()
    2. SPS_Link_Inputs = New clsLibNoDave
    3. SPS_Link_Inputs.Connect(SPS_IP)
    4. End Sub
    5. Public Sub SPS_Inputs()
    6. SPS_Connection_Inputs()
    7. Do
    8. Dim i = 0
    9. Dim bit(7) As Boolean
    10. For iByte As Integer = 0 To 9 'Eingangsbytes 0 - 9
    11. SPS_Link_Inputs.EB(iByte, bit)
    12. For iBit As Integer = 0 To 7
    13. If bit(iBit) Then
    14. osInputs(i).FillColor = Color.LimeGreen
    15. Else
    16. osInputs(i).FillColor = Color.WhiteSmoke
    17. End If
    18. i += 1
    19. Next
    20. Next
    21. Loop
    22. End Sub
    :/ Ohhhh mein Gott, was die Sonne alles ausmacht, kurze Pause eingelegt...schon war der Zusammenhang da.

    VB.NET-Quellcode

    1. Public Sub SPS_Inputs()
    2. SPS_Connection_Inputs()
    3. Do
    4. Dim i = 0
    5. Dim bit(7) As Boolean
    6. For iByte As Integer = 0 To 9 'Eingangsbytes 0 - 9
    7. SPS_Link_Inputs.EB(iByte, bit)
    8. For iBit As Integer = 0 To 7
    9. If bit(iBit) Then
    10. Me.Invoke(Sub() myInvokingSub(i, True))
    11. Else
    12. Me.Invoke(Sub() myInvokingSub(i, False))
    13. End If
    14. i += 1
    15. Next
    16. Next
    17. Loop
    18. End Sub
    19. Private Sub myInvokingSub(ByVal osNr As Integer, ByVal Status As Boolean)
    20. If Status Then
    21. osInputs(osNr).FillColor = Color.LimeGreen
    22. Else
    23. osInputs(osNr).FillColor = Color.WhiteSmoke
    24. End If
    25. End Sub




    ErfinderDesRades schrieb:


    VB.NET-Quellcode

    1. Me.BeginInvoke(Sub() MyInvokingSub(a, b, c))

    Mit BeginInvoke bekomme ich
    In System.ArgumentOutOfRangeException ist eine Ausnahme vom Typ "mscorlib.dll" aufgetreten, doch wurde diese im Benutzercode nicht verarbeitet.

    Zusätzliche Informationen: Der Index lag außerhalb des Bereichs. Er muss nicht negativ und kleiner als die Auflistung sein.



    Mit

    VB.NET-Quellcode

    1. Me.Invoke(Sub() myInvokingSub(i, False))

    läuft es...

    jetzt bin ich wirklich HAPPY :D


    Jetzt wird es langsam hell.... :thumbsup:

    RodFromGermany schrieb:

    Wenn dann eine Exception wegen thread-übergfreifendem Zugriff kommt, musst Du genau diese Zeile durch einen Invoke-Zugriff ersetzen.


    Danke @RodFromGermany und @ErfinderDesRades

    Jetzt verstehe ich den Zusammenhang / Prinzip ein bisschen besser.

    Bitte um korrektur, falls ich es falsch wiedergebe...
    Invoke "verknüpft" eine Prozedur mit dem "MainThread", weil die Controls innerhalb der "MainThread" gezeichnet/erstellt werden, dürfen diese nicht von einem zweiten Thread "manipuliert" werden.

    VB.NET-Quellcode

    1. Me.Invoke(Sub() myInvokingSub(i, False)) 'Parameter mitgeben
    2. '
    3. '
    4. Private Sub myInvokingSub(ByVal osNr As Integer, ByVal Status As Boolean) ' In dieser Prozedur dürfen die Controls "manipuliert" werden, weil die Prozedur wieder zur Main gehört.


    Liege ich halbwegs richtig?

    newbie schrieb:

    Invoke "verknüpft" eine Prozedur mit dem "MainThread", weil die Controls innerhalb der "MainThread" gezeichnet/erstellt werden, dürfen diese nicht von einem zweiten Thread "manipuliert" werden.
    Die Parameter musst Du natürlich so einrichten wie Du sie brauchst, in meinem Beispiel hab ich einfach nur was reingeschrieben.
    Das eigentliche Problem ist, dass es bei MultiTasking mit mehreren Prozessorkernen theopraktisch vorkommen kann, dass 2 Threads echt gleichzeitig schreibend auf ein und dasselbe Control zugreifen könnten, und das muss natürlich verhindert werden.
    Für Zugriffe auf "normale Variablen" genügt da ein Lock(object).
    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!

    newbie schrieb:

    VB.NET-Quellcode

    1. Me.Invoke(Sub() myInvokingSub(i, False)) 'Parameter mitgeben


    Du meinst natürlich

    VB.NET-Quellcode

    1. Me.BeginInvoke(Sub() myInvokingSub(i, False)) 'Parameter mitgeben
    Weil hat ja @jvbsl bereits in post#16 ausgeführt, dass Invoke den NebenThread ausbremst.
    Und grade bei einer Sps-Steuerung ist das vermutlich das letzte, was du brauchen kannst.

    newbie schrieb:

    ErfinderDesRades schrieb:

    VB.NET-Quellcode

    1. Me.BeginInvoke(Sub() MyInvokingSub(a, b, c))



    Mit BeginInvoke bekomme ich

    In System.ArgumentOutOfRangeException ist eine Ausnahme vom Typ "mscorlib.dll" aufgetreten, doch wurde diese im Benutzercode nicht verarbeitet.

    Zusätzliche Informationen: Der Index lag außerhalb des Bereichs. Er muss nicht negativ und kleiner als die Auflistung sein.




    Mit

    VB.NET-Quellcode

    1. Me.Invoke(Sub() myInvokingSub(i, False))

    läuft es...


    ErfinderDesRades schrieb:

    Weil hat ja @jvbsl bereits in post#16 ausgeführt, dass Invoke den NebenThread ausbremst.
    Und grade bei einer Sps-Steuerung ist das vermutlich das letzte, was du brauchen kannst.

    Ja, das hatte ich vernommen. Kann ich das so verstehen, dass bei

    Quellcode

    1. BeginInvoke
    der Code weiter verarbeitet wird und bei Invoke der Code an der Stelle wartet bis der Zeiger wieder da ist, wo ich den Sprung in den MyInvokingSub
    vorgenommen habe?

    Also ich muss sagen die Geschwindigkeit ist ok. Relative Zeitnah (Wimpernschlag) kommen die Signalzustände auf den Bildschirm.

    ErfinderDesRades schrieb:

    Dann such und beheb doch besser den Fehler, statt mit Try-Error "Ah - so stürztes nicht ab" zu coden.


    Ok mach ich, danke

    Edit:
    Witzig ist das mit BeginInvoke der Fehler kommt
    In System.ArgumentOutOfRangeException ist eine Ausnahme vom Typ "mscorlib.dll" aufgetreten, doch wurde diese im Benutzercode nicht verarbeitet.

    Zusätzliche Informationen: Der Index lag außerhalb des Bereichs. Er muss nicht negativ und kleiner als die Auflistung sein.

    osNr hat in dem Moment "80", was ja auch richtig ist eigentlich. Habe ja 80 OvalShapes für die Eingänge EB0 - EB9 (10x8=80)


    Füge ich das hier ein...

    VB.NET-Quellcode

    1. Me.BeginInvoke(Sub() myInvoke_SPS_Inputs(i, True))
    2. Threading.Thread.Sleep(1)

    läuft es anstandslos.

    Oder halt nur BeginInvoke Das verstehe ich jetzt auch nicht.


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

    ErfinderDesRades schrieb:

    Der höchste zulässige Index ist 79


    Richtig, weil die List(Of T) 0-basierent indiziert ist.

    Ich kann mir trotzdem nicht erklären, warum es mit Me.BeginInvoke nicht läuft...und davon ab ist mir gestern Abend noch aufgefallen das die Me.BeginInvoke wesentlich / deutlich langsamer ist als die Me.Invoke

    Mit BeginInvoke lief es halbwegs indem ich osInputs(osNr - 1).FillColor = ... schrieb, allerdings entsprachen die OvalShapes nicht mehr den tatsächlichen SPS-Eingänge

    Meine Frage von gestern noch...

    newbie schrieb:

    Kann ich das so verstehen, dass bei
    BeginInvoke der Code weiter verarbeitet wird und bei Invoke der Code an der Stelle wartet bis der Zeiger wieder da ist, wo ich den Sprung in den MyInvokingSub vorgenommen habe?

    Sprich BeginInvoke asyncron, wäre dann Invoke syncron?

    newbie schrieb:

    Sprich BeginInvoke asyncron, wäre dann Invoke syncron?
    jo, aber von der anderen Seite aus betrachtet.
    Und ich würd nicht mit Zeigern und Sprüngen formulieren, sondern der Nebenthread blockiert so lange, bis der MainThread die invokete Methode abgearbeitet hat.
    Bei BeginInvoke hingegen kann der Nebenthread in seiner Arbeit fortfahren, während der MainThread die invokete Methode abarbeitet - was ja normal der Sinn von Nebenläufigkeit ist.

    Was ist nun mit meiner Frage: Ist

    VB.NET-Quellcode

    1. Me.BeginInvoke(Sub() myInvoke_SPS_Inputs(i, True))
    wirklich die Fehlerzeile, und das wirklich mit der Fehlermeldung?
    Obwohl in der Zeile gar nix indiziertes vorgeht?

    Was zB Sinn ergäbe, da du ja sagst, es seien zur Laufzeit generierte Controls - dass die Sps-Steuerung bereits mit 80 Daten ankommt, während der HauptThread seine 80 Controls noch garnet fertig-generiert hat.
    Aber das müsste eiglich innerhalb der myInvoke_SPS_Inputs()-Methode crashen, nicht beim Aufruf derselben.

    ErfinderDesRades schrieb:


    Ist wirklich die Fehlerzeile, und das wirklich mit der Fehlermeldung?
    Obwohl in der Zeile gar nix indiziertes vorgeht?


    Nein, nicht das wir aneinander vorbeireden. die Fehlerzeile ist...

    VB.NET-Quellcode

    1. Private Sub myInvokingSub(ByVal osNr As Integer, ByVal Status As Boolean)
    2. If Status Then
    3. osInputs(osNr).FillColor = Color.LimeGreen ' mal hier...
    4. Else
    5. osInputs(osNr).FillColor = Color.WhiteSmoke ' ...oder mal da (abhängig vom Zustand des SPS Eingangs)
    6. End If
    7. End Sub


    Dies betrifft nicht das Testprojekt sondern das Hauptprojekt. Im Testprojekt sind es Ovalshapes (Im Designer erstellt) im Hauptprojekt sind es Panels (zur Laufzeit generiert werden) in einer Picturebox.

    ErfinderDesRades schrieb:

    Laufzeit generierte Controls

    newbie schrieb:

    die Fehlerzeile ist...
    Lass mich raten: Wie ist osInputs deklariert und instanziiert?
    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!
    Also ich für mein Teil weiß den Fehler schon: Der Index liegt außerhalb des gültigen Bereichs.
    sieh einfach im Lokalfenster nach, wieviele Elemente osInputs() hat - dann kennst du den gültigen Bereich.

    Lokalfenster? : VisualStudio richtig nutzen (Google ist nicht deine Mami)

    Übrigens ist ärgerlich, dass du uns zur Fehlermeldung eine ganz andere Codezeile vorsetzst. :cursing:

    ErfinderDesRades schrieb:

    Übrigens ist ärgerlich, dass du uns zur Fehlermeldung eine ganz andere Codezeile vorsetzst. :cursing:


    Sry, war keine Absicht.

    newbie schrieb:

    weil die List(Of T) 0-basierent indiziert ist.

    hatte ich aber schon vorher geschrieben. 80 Ovalshapes(0-79)

    VB.NET-Quellcode

    1. osInputs = New List(Of OvalShape)
    2. osInputs.AddRange({OS_E00, OS_E01, OS_E02, OS_E03, OS_E04, OS_E05, OS_E06, OS_E07})
    3. osInputs.AddRange({OS_E10, OS_E11, OS_E12, OS_E13, OS_E14, OS_E15, OS_E16, OS_E17})
    4. osInputs.AddRange({OS_E20, OS_E21, OS_E22, OS_E23, OS_E24, OS_E25, OS_E26, OS_E27})
    5. osInputs.AddRange({OS_E30, OS_E31, OS_E32, OS_E33, OS_E34, OS_E35, OS_E36, OS_E37})
    6. osInputs.AddRange({OS_E40, OS_E41, OS_E42, OS_E43, OS_E44, OS_E45, OS_E46, OS_E47})
    7. osInputs.AddRange({OS_E50, OS_E51, OS_E52, OS_E53, OS_E54, OS_E55, OS_E56, OS_E57})
    8. osInputs.AddRange({OS_E60, OS_E61, OS_E62, OS_E63, OS_E64, OS_E65, OS_E66, OS_E67})
    9. osInputs.AddRange({OS_E70, OS_E71, OS_E72, OS_E73, OS_E74, OS_E75, OS_E76, OS_E77})
    10. osInputs.AddRange({OS_E80, OS_E81, OS_E82, OS_E83, OS_E84, OS_E85, OS_E86, OS_E87})
    11. osInputs.AddRange({OS_E90, OS_E91, OS_E92, OS_E93, OS_E94, OS_E95, OS_E96, OS_E97})


    ;( sry nochmal

    ErfinderDesRades schrieb:

    hast du mal ins Lokalfenster geguckt


    Ja 80 deswegen überlauf bzw. OutOfRange weil ja max. 79. Ich kann mir nicht erklären warum das so ist. Aber nochmal um missverständnisse auszuräumen. Der Fehler tritt nur bei BeginInvoke auf nicht bei Invoke
    Was allerding mit BeginInvoke läuft ist die folgende Combo...

    VB.NET-Quellcode

    1. Me.BeginInvoke(Sub() myInvoke_SPS_Inputs(i, True))
    2. Threading.Thread.Sleep(1) 'Wenn ich 1 ms warten lassen dann ist alles gut.


    Rein von der Schleife kann theoretisch i niemals 80 werden.

    VB.NET-Quellcode

    1. Public Sub SPS_Inputs()
    2. SPS_Connection_Inputs()
    3. Do
    4. Dim i = 0
    5. Dim bit(7) As Boolean
    6. For iByte As Integer = 0 To 9 'Eingangsbytes 0 - 9
    7. SPS_Link_Inputs.EB(iByte, bit)
    8. For iBit As Integer = 0 To 7 'Eingangbits 0 - 7
    9. If bit(iBit) Then
    10. Me.Invoke(Sub() myInvoke_SPS_Inputs(i, True))
    11. Else
    12. Me.Invoke(Sub() myInvoke_SPS_Inputs(i, False))
    13. End If
    14. i += 1 'i max. = 79 (80 Schleifen durchläufe, Start = 0)
    15. Next
    16. Next
    17. Loop
    18. End Sub

    newbie schrieb:

    VB.NET-Quellcode

    1. If bit(iBit) Then
    2. Me.Invoke(Sub() myInvoke_SPS_Inputs(i, True))
    3. Else
    4. Me.Invoke(Sub() myInvoke_SPS_Inputs(i, False))
    5. End If
    machst Du

    VB.NET-Quellcode

    1. Me.Invoke(Sub() myInvoke_SPS_Inputs(i, bit(iBit)))
    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!

    newbie schrieb:

    Rein von der Schleife kann theoretisch i niemals 80 werden.
    Eine gewagte Theorie, wenn die Praxis dagegen spricht ;)

    aber die Schleife ist eh müll.
    Da wird ja in einem Dauer-Loop ununterbrochen invoked, für jedes einzelne Bit immer von neuem.
    Du solltest es so coden, dass nur dann invoked wird, wenn auch eine Änderung stattgefunden hat.

    Und der Endlos-Loop ist erst recht die Katastrophe. Ja, vlt. eine Thread.Sleep(1) einbauen, sonst ist ein Kernel damit ausgelastet.

    Wie gesagt: es reicht (und wäre 300mal resourcensparender), wenn das Gui nur alle 300ms geupdated wird.

    Edit: noch mehr Müll:
    Ganz offensichtlich tickt deine Sps auch so, wie die in meine Siemens-Übung.
    Also Threading ist hier ühaupt nicht erforderlich.

    Mach statt des DauerLoops im NebenThread einfach einen WinFormsTimer (der ja im GuiThread läuft), und lies die Sps einfach im Gui-Thread aus - ohne Threading.

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

    OK
    das war ursprünglich auch mit einem Timer. Bei dem Testprojekt sehe ich es auch ein. Aber mein Hauptprojekt ist etwas Umfangreicher, daher die Idee (Neugier) mit dem Multithread.

    Ich sehe ein, der Einsatz ist "Müll", aber..
    Wissen setzt Neugier vorraus


    Muss euch nochmal mit dem Hauptthema des Treads nerven. Hauptprojekt, da werden die Panels zur Laufzeit in einer Picturebox erzeugt.

    VB.NET-Quellcode

    1. Private Sub frmMain_Load(sender As Object, e As System.EventArgs) Handles Me.Load
    2. Me.Inputs_Thread = New Threading.Thread(New ThreadStart(AddressOf Me.InputsList))
    3. Me.Inputs_Thread.Start()
    4. End Sub
    5. Private Sub InputsList()
    6. Me.LoadXML(MVPic)
    7. End Sub
    8. Public Sub LoadXML(ByVal [MVpicture] As String)
    9. If Me.PicMV.InvokeRequired Then
    10. Dim d As New PicMV_Load(AddressOf LoadXML)
    11. Me.Invoke(d, New Object() {[MVpicture]})
    12. Else
    13. Sensor_DataSet.Sensor_Table.Clear()
    14. Me.Sensor_DataSet.ReadXml(DataFile)
    15. Me.PicMV.Image = System.Drawing.Image.FromFile([MVpicture])
    16. Me.PicMV.Refresh()
    17. For Each row In Sensor_DataSet.Sensor_Table
    18. Dim newSensor As New Panel
    19. Dim loc, locProzet As Point
    20. locProzet.X = CInt(100 / CDbl(row.P_Width) * CDbl(row.Left))
    21. locProzet.Y = CInt(100 / CDbl(row.P_Height) * CDbl(row.Top))
    22. loc.X = CInt((Me.PicMV.Width / 100 * locProzet.X) + (SensGr / 2))
    23. loc.Y = CInt((Me.PicMV.Height / 100 * locProzet.Y) + (SensGr / 2))
    24. newSensor = newpanel.CreatePanel(SensGr, Me.PicMV.PointToScreen(Me.PicMV.PointToClient(loc)), SensorAus)
    25. newSensor.Tag = row.Inv
    26. newSensor.Name = row.BMK
    27. Me.PicMV.Controls.Add(newSensor)
    28. newSensor.BringToFront()
    29. AddHandler newSensor.Paint, AddressOf newSensor_paint
    30. Next
    31. Me.Invoke(Sub() SPS_Inputs(PicMV)) 'Das Invoke ist hier irgenwie nicht Richtig glaube ich.
    32. End If
    33. End Sub
    34. Public Sub SPS_Inputs(ByVal myPicBox As PictureBox)
    35. SPS_Connection_Inputs()
    36. Do
    37. Dim EB As String
    38. For Each Sens As Panel In myPicBox.Controls()
    39. EB = Strings.Mid(Sens.Name, 2)
    40. Dim SPS_Port() = Split(EB, ".")
    41. Dim Invert = CType(Sens.Tag, Boolean)
    42. If SPS_Link_Inputs.Eingang(CInt(SPS_Port(0)), CInt(SPS_Port(1))) Then
    43. If Invert Then
    44. Sens.BackgroundImage = SensorAus
    45. Else
    46. Sens.BackgroundImage = SensorEin
    47. End If
    48. Else
    49. If Invert Then
    50. Sens.BackgroundImage = SensorEin
    51. Else
    52. Sens.BackgroundImage = SensorAus
    53. End If
    54. End If
    55. Next
    56. Loop
    57. End Sub


    Hier ist es etwas komplizierter als in dem Testprojekt, könntet ihr mir nochmal etwas helfen bitte.
    Ich bekomme noch nicht mal eine Fehlermeldung Programm friert ein.

    MfG
    ja, da ist auch ein endlos-loop drinne - diesmal im Gui-Thread laufend (was logischerweise deshalb blockiert).
    endlos-loops sind müll -dabei kann man nicht helfen.

    Entferne das gesamte Threding-Gedöns, entferne den EndlessLoop, und mach diese For each sens-Schleife statt im Endlos-Loop in einem Timer-Tick.