none
Problem - Wahrscheinlich 'threadübergreifender Zugriff' auf Controls .... (mehrere) RRS feed

  • Frage

  • Zum Thema 'threadübergeifende Zugriffe habe ich schon einiges gelesen. Ich vermute mal, dass ich genau dieses problem habe :-(

    Meine Postings drehen sich derzeit meist immer im ähnliche Problemkomplexe.

    Ich habe eine Schaltung, die Daten über den CAN-Bus sendet oder empfängt und diese Daten über serielle Schnittstelle an den PC weiterleitet - immer in Paketen zu 16 Byte.

    Auf dem PC existiert eine Klasse in VB.NEt  genannt CANBUS, die wiederum vom COM-Port ein Ereignis bekommt wenn 16 Byte da sind.

    Dann werden die Daten ausgewertet (CRC kontrollieren, etc.) und falls alles in Ordnung, ist unterscheidliche Events ausgelöst. Meist ist das ein Ereignis, wenn normale Datenpakete eingetroffen sind; es gibt aber auch Ereignisse über Fehlermeldungen u.a.

    Eine einfache Testapplikation hat ein Fenster mit zahlreichen Textboxen, in denen die Daten dargestellt werden, sowie ein Datagrid.

    Die Applikation instanziirt die Klasse CANBUS mit WithEvents und bekommt pormpt ein Ereignis, sobald neue Dtaen angekommen sind.

    Im Ereignis-Handler werden dann die Daten in den Textboxen dargestellt und das Datagridview aktualisiert.

    Zu Beginn habe ich den Kopf mittels 'CheckforIllegalThreadCalls=false' aus der Schlinge gezogen. BeimProbieren mit einzelnen Datenpaketen in grösseren Abständen ging das prima.

    In 'real life' kommen die Daten aber recht schnell hintereinander. Manchmal blockiert die ANwendung nach 60-100 Paketen, manchmal läuft's aber auch lange Zeit problemlos und nur der vertikale Balken im Datagridview hängt.

    Wenn ich dann die Anweisung CheckforilllegalThreadcalls=true setze oder rausnehme dann ist es ganz aus.

    Ich habe schon einiges zum Thema gelesen, u.a. auch auf gssg.de und über den BackgroundWorker.

    Zugegeben, das klingt alles ganz schön kompliziert. Am sympathischtesten ist mir der Backgroundworker, abe ich bin mir nicht sicher wie ich den anwenden muss.

    Das Ereignis CANBUS.FrameReceived enthält grob folgende Anweisungen:

    AktualisiereTExtboxen(msg)   'Date in Textboxen darstellen

    AktualisiereFehlerFlags(errnr)   ' Bei Fehlern Hintergrundfarbe von Labels ändern

    AktualisiereDataGrid(msg)      '

    Wie müsste die Geschichte mit dem BackgroundWorker funktionieren ? Das Ereignis mit RunworkerCompleted scheint mir zu spät zu kommen. Und was ist mit DoWork ?

    Samstag, 18. Dezember 2010 16:27

Antworten

  • Im Ereignis-Handler werden dann die Daten in den Textboxen dargestellt und das Datagridview aktualisiert.

    Hallo Nico,

    Du hast viele Fragen. Nur dazu: in einer Datareceived Event Routine darfst Du nicht direkt auf ein GUI Element der Form zugreifen. Diese Routine wird in einen sekundären thread angehoben. Dieses ist so direkt nicht erkennbar. Hier ein Codeausschnitt von mir, wie man damit umgeht. Die Empfangsdaten müssen zunächst in einen privat deklarierten buffer der Form geschrieben werden. Von dort kannst Du sie dann in ein GUI schreiben. (Hier RX_buffer.text TextBox)

    zu deinen anderen Fragen: ich lese mir das noch einmal durch. Probiere erst einmal das hier.

    schöne Grüße Ellen

    P.S. Welches CAN Bus Interface benutzt Du?

     Nachtrag: Hier die msdn Doku zu diesem Thema. Dasselbe, was ich schon gesagt habe uvm. ....

    http://msdn.microsoft.com/de-de/library/system.io.ports.serialport.datareceived.aspx

     ' -------------------------------------------------------------------
     '  write receive data to TextBox
     '--------------------------------------------------------------------
     Public Sub DoUpdate(ByVal sender As Object, ByVal e As System.EventArgs)
    
     RxD_buffer.Text = readBuffer
     
     End Sub
     '---------------------------------------------------------------------
     '   read COM Port
     '---------------------------------------------------------------------
     Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, _
          ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _
          Handles SerialPort1.DataReceived
     If comOpen Then
      Try
    
      ' reads string until .Newline property 
      readBuffer = SerialPort1.ReadLine()
      
      'data to form1
      Me.Invoke(New EventHandler(AddressOf DoUpdate))
    
      Catch ex As IO.InvalidDataException
      MsgBox(ex.Message)
      End Try
     End If
     End Sub
    

    Ich benutze/ I'm using VB2008 & VB2010
    Samstag, 18. Dezember 2010 17:04
  • Hallo,

    Zum Thema 'threadübergeifende Zugriffe habe ich schon
    einiges gelesen. Ich vermute mal, dass ich genau dieses
    problem habe :-(

    Das Problem bekommst Du z.B. dann, wenn Du versuchst
    auf eine Form oder ein Steuerelement zuzugreifen, das in
    einem anderen als dem zugreifenden Thread erstellt worden
    ist.
    Das ist z.B. dann der Fall, wenn Deine Form u. Steuerelemente
    wie meist üblich im Hauptthread erstellt worden sind und ein
    weiterer (separater) Thread versucht auf die Form oder eines
    der Steuerelemente zuzugreifen.

    Meine Postings drehen sich derzeit meist immer im ähnliche
    Problemkomplexe.

    Ich habe eine Schaltung, die Daten über den CAN-Bus sendet
    oder empfängt und diese Daten über serielle Schnittstelle an
    den PC weiterleitet - immer in Paketen zu 16 Byte.

    Das SerialPort-Objekt, mit dem Du vermutlich Deine Zugriffe auf
    die ser. Schnittstelle realisierst arbeitet asynchron, also in einem
    separaten Thread. Löst SerialPort das Ereignis _DataReceived
    aus, so geschieht dies in eben diesem separaten Thread und
    ein direkter Zugriff auf Deine Form oder eines der darauf befindlichen
    Steuerelemente ist dann, weil threadübergreifend, nicht
    zulässig und muss erst mal entsprechend mit dem Hauptthread
    synchronisiert werden.

    Auf dem PC existiert eine Klasse in VB.NEt genannt CANBUS,
    die wiederum vom COM-Port ein Ereignis bekommt wenn 16 Byte
    da sind.

    Ich vermute mal, mit "Ereignis" meinst Du das _DataReceived-
    Ereignis des SerialPorts.

    Dann werden die Daten ausgewertet (CRC kontrollieren, etc.) und
     falls alles in Ordnung, ist unterscheidliche Events ausgelöst.
    Meist ist das ein Ereignis, wenn normale Datenpakete eingetroffen
    sind; es gibt aber auch Ereignisse über Fehlermeldungen u.a.

    Eine einfache Testapplikation hat ein Fenster mit zahlreichen
    Textboxen, in denen die Daten dargestellt werden, sowie ein Datagrid.

    Und an dieser Stelle bekommst Du Dein Problem mit dem
    threadübergreifenden Zugriff, da der im Ereignis
    SerialPort_DataReceived angestossene Code eben nicht im
    Hauptthread, der auch Eigentümer Deiner Form und Controls
    ist, sondern in einem eigenen (separaten) Thread läuft.

    Die Applikation instanziirt die Klasse CANBUS mit WithEvents
    und bekommt pormpt ein Ereignis, sobald neue Dtaen angekommen
    sind.

    Im Ereignis-Handler werden dann die Daten in den Textboxen
    dargestellt und das Datagridview aktualisiert.

    Der Zugriff auf diese Textboxen und das DGV darf aber nicht
    direkt aus dem separaten Thread (_DataReveived) erfolgen,
    sondern muss erst mit dem Hauptthread synchronisiert werden.

    Zu Beginn habe ich den Kopf mittels
    'CheckforIllegalThreadCalls=false' aus der Schlinge gezogen.

    Das wäre eine recht unschöne Holzhammermethode, die Du
    besser nicht anwenden solltest, weil das durchaus zu Problemen
    führen kann.

    BeimProbieren mit einzelnen Datenpaketen in grösseren
    Abständen ging das prima.
    In 'real life' kommen die Daten aber recht schnell hintereinander.
    Manchmal blockiert die ANwendung nach 60-100 Paketen,
    manchmal läuft's aber auch lange Zeit problemlos und nur der
    vertikale Balken im Datagridview hängt.

    Irgendwann gibt es Konflikte zwischen Hauptthread und zweitem
    Thread beim Zugriffen auf Deine Form bzw. Controls.

    Wenn ich dann die Anweisung CheckforilllegalThreadcalls=true
    setze oder rausnehme dann ist es ganz aus.

    Dann musst Du eben "sauber" arbeiten und Deine Zugriffe
    aus dem separaten Thread "threadsicher" machen.

    Ich habe schon einiges zum Thema gelesen, u.a. auch auf
    gssg.de und über den BackgroundWorker.

    Der BackgroundWorker wäre eine Möglichkeit, es geht
    aber auch ohne.

    Hier mal ein Beispiel, das den Zugriff aus zwei verschiedenen
    Threads (Hauptthread und separater Thread mTH) auf ein
    Label-Steuerelement zeigt.

    Kopiere nachfolgenden Code in ein leeres Formmodul (Form1.vb).

    ' / / /  Beginn Code Form1.vb
    Imports System.Threading
    Public Class Form1
        Private WithEvents tbIn As TextBox
        Private WithEvents lblOut As Label
        Private mTH As Thread
        Private mUnloadFlag As Boolean

        Private Delegate Sub DShowText _
                (ByVal C As Control, _
                 ByVal Text As String)

        Private Sub Form1_Load _
                (ByVal sender As System.Object, _
                 ByVal e As System.EventArgs _
                ) Handles MyBase.Load

            CreateControls()
        End Sub

        Private Sub Form1_Shown _
                (ByVal sender As Object, _
                 ByVal e As System.EventArgs _
                ) Handles Me.Shown

            ' zweiten Thread starten
            mTH = New Thread(AddressOf ThreadProc)
            mTH.Priority = ThreadPriority.BelowNormal
            mTH.Start()
        End Sub

        Private Sub Form1_FormClosing _
            (ByVal sender As Object, _
             ByVal e As FormClosingEventArgs _
            ) Handles Me.FormClosing

            ' fallse der zweite Thread noch aktiv ist, ...
            If mTH.IsAlive Then
                mUnloadFlag = True

                ' ... warten bis zweiter Thread beendet ist
                Console.WriteLine "Warten ..."
                mTH.Join()
                Console.WriteLine "... und weiter gehts."
            End If
        End Sub

        Private Sub CreateControls()
            tbIn = New TextBox
            With tbIn
                .Name = "tbIn"
                .Font = New Font("Arial", 12)
                .Text = "bitte Text eingeben"

                .SetBounds _
                        (10, 10, _
                         Me.ClientSize.Width - 20, _
                         .Height)

                .Anchor = _
                        AnchorStyles.Left Or _
                        AnchorStyles.Top Or _
                        AnchorStyles.Right

            End With
            Me.Controls.Add(tbIn)

            lblOut = New Label
            With lblOut
                .Name = "lblOut"
                .Font = tbIn.Font
                .AutoSize = True

                .Location = _
                    New Point(10, tbIn.Bottom + 20)

                .Text = "....."
            End With
            Me.Controls.Add(lblOut)
        End Sub

        Private Sub ShowText _
                (ByVal C As Control, _
                 ByVal Text As String)

            If C.InvokeRequired Then
                ' Aufruf erfolgte aus fremdem Thread
                Dim D As New DShowText(AddressOf ShowText)

                Dim PArray() As Object = _
                    New Object() {lblOut, Text}

                ' Sub ShowText erneut über den
                ' Delegaten DShowText aufrufen
                C.Invoke(D, PArray)
            Else
                ' Aufruf erfolgte aus dem Hauptthread
                C.Text = Text
            End If
        End Sub

        Private Sub ThreadProc()
            ' Diese Prozedur wird in einem zweiten Thread
            ' ausgeführt solange mUnloadFlag = False ist.
            ' Sub verlassen, wenn mUnloadFlag auf True
            ' gesetzt ist.

            Do While Not mUnloadFlag
                ShowText(lblOut, Now.ToLongTimeString)
                Thread.Sleep(3000)
            Loop
        End Sub

        Private Sub tbIn_TextChanged _
                (ByVal sender As Object, _
                 ByVal e As System.EventArgs _
                ) Handles tbIn.TextChanged

            If lblOut IsNot Nothing Then
                ShowText(lblOut, tbIn.Text)
            End If
        End Sub

    End Class
    ' \ \ \  E N T E

    Nach dem Programmstart siehst Du die Form mit einer
    Textbox und einem darunter angeordneten Label.

    Nach dem Programmstart wird auch gleich ein zweiter
    Thread (mTH) gestartet, der alle 3 Sekunden die akt.
    Uhrzeit in das Label schreibt.

    Wenn Du Text in die Textbox eingibst, wird dieser
    direkt im Label angezeigt. Dieser Zugriff auf das
    Label-Steuerelement (lblOut) erfolgt aus dem Hauptthread
    und kann somit direkt ohne Umweg erfolgen.

    Der Zugriff des zweiten Threads auf das Label-Steuerelement
    zum Anzeigen der Uhrzeit kann, weil threadübergreifender
    Zugriff, nicht direkt erfolgen sondern muss in der Sub ShowText
    im Abfragezweig

        if C.InvokeRequired then

    mit dem Hauptthread synchronisiert werden. Dies geschieht
    indem die Sub ShowText einfach über den Delegaten
    DShowText erneut aufgerufen wird.

    Wird die Form entladen, um das Programm zu beenden,
    wird in Form_Closing geprüft, ob der zweite Thread noch am
    Leben ist (mTH.IsAlive = True) und falls ja, muss diesem
    zweiten Thread mitgeteilt werden, dass er sich beenden
    soll  (mUnloadFlag = True) und solange gewartet
    werden (mTH.Join), bis dieser zweite Thread tatsächlich
    beendet ist Nachdem dies geschehen ist, wird die Forrm
    tatsächlich entladen und das Programm damit beendet.

    Gruß aus St.Georgen
    Peter Götz
    www.gssg.de (mit VB-Tipps u. Beispielprogrammen)

    Sonntag, 19. Dezember 2010 10:14

Alle Antworten

  • Im Ereignis-Handler werden dann die Daten in den Textboxen dargestellt und das Datagridview aktualisiert.

    Hallo Nico,

    Du hast viele Fragen. Nur dazu: in einer Datareceived Event Routine darfst Du nicht direkt auf ein GUI Element der Form zugreifen. Diese Routine wird in einen sekundären thread angehoben. Dieses ist so direkt nicht erkennbar. Hier ein Codeausschnitt von mir, wie man damit umgeht. Die Empfangsdaten müssen zunächst in einen privat deklarierten buffer der Form geschrieben werden. Von dort kannst Du sie dann in ein GUI schreiben. (Hier RX_buffer.text TextBox)

    zu deinen anderen Fragen: ich lese mir das noch einmal durch. Probiere erst einmal das hier.

    schöne Grüße Ellen

    P.S. Welches CAN Bus Interface benutzt Du?

     Nachtrag: Hier die msdn Doku zu diesem Thema. Dasselbe, was ich schon gesagt habe uvm. ....

    http://msdn.microsoft.com/de-de/library/system.io.ports.serialport.datareceived.aspx

     ' -------------------------------------------------------------------
     '  write receive data to TextBox
     '--------------------------------------------------------------------
     Public Sub DoUpdate(ByVal sender As Object, ByVal e As System.EventArgs)
    
     RxD_buffer.Text = readBuffer
     
     End Sub
     '---------------------------------------------------------------------
     '   read COM Port
     '---------------------------------------------------------------------
     Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, _
          ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _
          Handles SerialPort1.DataReceived
     If comOpen Then
      Try
    
      ' reads string until .Newline property 
      readBuffer = SerialPort1.ReadLine()
      
      'data to form1
      Me.Invoke(New EventHandler(AddressOf DoUpdate))
    
      Catch ex As IO.InvalidDataException
      MsgBox(ex.Message)
      End Try
     End If
     End Sub
    

    Ich benutze/ I'm using VB2008 & VB2010
    Samstag, 18. Dezember 2010 17:04
  • Uff ! Das klingt zu einfach ! Fast zu schön um wahr zu sein. Ich werde es probieren

    Als CAN-Interface wird ein AT90CAN32 benutzt. Eigentlich too much; die Schaltung ist aber als Spin-off einer anderen Anwednung entstanden, die regen Gebrauch dr CAN-Mailboxen macht. Ansonsten wäre tewas anderes sicher noch einfacher.

    Die Übetragung über die UART-Schnittstelle erfolgt mit einem FT232 (also USB)

    Samstag, 18. Dezember 2010 17:19
  • Hallo,

    Zum Thema 'threadübergeifende Zugriffe habe ich schon
    einiges gelesen. Ich vermute mal, dass ich genau dieses
    problem habe :-(

    Das Problem bekommst Du z.B. dann, wenn Du versuchst
    auf eine Form oder ein Steuerelement zuzugreifen, das in
    einem anderen als dem zugreifenden Thread erstellt worden
    ist.
    Das ist z.B. dann der Fall, wenn Deine Form u. Steuerelemente
    wie meist üblich im Hauptthread erstellt worden sind und ein
    weiterer (separater) Thread versucht auf die Form oder eines
    der Steuerelemente zuzugreifen.

    Meine Postings drehen sich derzeit meist immer im ähnliche
    Problemkomplexe.

    Ich habe eine Schaltung, die Daten über den CAN-Bus sendet
    oder empfängt und diese Daten über serielle Schnittstelle an
    den PC weiterleitet - immer in Paketen zu 16 Byte.

    Das SerialPort-Objekt, mit dem Du vermutlich Deine Zugriffe auf
    die ser. Schnittstelle realisierst arbeitet asynchron, also in einem
    separaten Thread. Löst SerialPort das Ereignis _DataReceived
    aus, so geschieht dies in eben diesem separaten Thread und
    ein direkter Zugriff auf Deine Form oder eines der darauf befindlichen
    Steuerelemente ist dann, weil threadübergreifend, nicht
    zulässig und muss erst mal entsprechend mit dem Hauptthread
    synchronisiert werden.

    Auf dem PC existiert eine Klasse in VB.NEt genannt CANBUS,
    die wiederum vom COM-Port ein Ereignis bekommt wenn 16 Byte
    da sind.

    Ich vermute mal, mit "Ereignis" meinst Du das _DataReceived-
    Ereignis des SerialPorts.

    Dann werden die Daten ausgewertet (CRC kontrollieren, etc.) und
     falls alles in Ordnung, ist unterscheidliche Events ausgelöst.
    Meist ist das ein Ereignis, wenn normale Datenpakete eingetroffen
    sind; es gibt aber auch Ereignisse über Fehlermeldungen u.a.

    Eine einfache Testapplikation hat ein Fenster mit zahlreichen
    Textboxen, in denen die Daten dargestellt werden, sowie ein Datagrid.

    Und an dieser Stelle bekommst Du Dein Problem mit dem
    threadübergreifenden Zugriff, da der im Ereignis
    SerialPort_DataReceived angestossene Code eben nicht im
    Hauptthread, der auch Eigentümer Deiner Form und Controls
    ist, sondern in einem eigenen (separaten) Thread läuft.

    Die Applikation instanziirt die Klasse CANBUS mit WithEvents
    und bekommt pormpt ein Ereignis, sobald neue Dtaen angekommen
    sind.

    Im Ereignis-Handler werden dann die Daten in den Textboxen
    dargestellt und das Datagridview aktualisiert.

    Der Zugriff auf diese Textboxen und das DGV darf aber nicht
    direkt aus dem separaten Thread (_DataReveived) erfolgen,
    sondern muss erst mit dem Hauptthread synchronisiert werden.

    Zu Beginn habe ich den Kopf mittels
    'CheckforIllegalThreadCalls=false' aus der Schlinge gezogen.

    Das wäre eine recht unschöne Holzhammermethode, die Du
    besser nicht anwenden solltest, weil das durchaus zu Problemen
    führen kann.

    BeimProbieren mit einzelnen Datenpaketen in grösseren
    Abständen ging das prima.
    In 'real life' kommen die Daten aber recht schnell hintereinander.
    Manchmal blockiert die ANwendung nach 60-100 Paketen,
    manchmal läuft's aber auch lange Zeit problemlos und nur der
    vertikale Balken im Datagridview hängt.

    Irgendwann gibt es Konflikte zwischen Hauptthread und zweitem
    Thread beim Zugriffen auf Deine Form bzw. Controls.

    Wenn ich dann die Anweisung CheckforilllegalThreadcalls=true
    setze oder rausnehme dann ist es ganz aus.

    Dann musst Du eben "sauber" arbeiten und Deine Zugriffe
    aus dem separaten Thread "threadsicher" machen.

    Ich habe schon einiges zum Thema gelesen, u.a. auch auf
    gssg.de und über den BackgroundWorker.

    Der BackgroundWorker wäre eine Möglichkeit, es geht
    aber auch ohne.

    Hier mal ein Beispiel, das den Zugriff aus zwei verschiedenen
    Threads (Hauptthread und separater Thread mTH) auf ein
    Label-Steuerelement zeigt.

    Kopiere nachfolgenden Code in ein leeres Formmodul (Form1.vb).

    ' / / /  Beginn Code Form1.vb
    Imports System.Threading
    Public Class Form1
        Private WithEvents tbIn As TextBox
        Private WithEvents lblOut As Label
        Private mTH As Thread
        Private mUnloadFlag As Boolean

        Private Delegate Sub DShowText _
                (ByVal C As Control, _
                 ByVal Text As String)

        Private Sub Form1_Load _
                (ByVal sender As System.Object, _
                 ByVal e As System.EventArgs _
                ) Handles MyBase.Load

            CreateControls()
        End Sub

        Private Sub Form1_Shown _
                (ByVal sender As Object, _
                 ByVal e As System.EventArgs _
                ) Handles Me.Shown

            ' zweiten Thread starten
            mTH = New Thread(AddressOf ThreadProc)
            mTH.Priority = ThreadPriority.BelowNormal
            mTH.Start()
        End Sub

        Private Sub Form1_FormClosing _
            (ByVal sender As Object, _
             ByVal e As FormClosingEventArgs _
            ) Handles Me.FormClosing

            ' fallse der zweite Thread noch aktiv ist, ...
            If mTH.IsAlive Then
                mUnloadFlag = True

                ' ... warten bis zweiter Thread beendet ist
                Console.WriteLine "Warten ..."
                mTH.Join()
                Console.WriteLine "... und weiter gehts."
            End If
        End Sub

        Private Sub CreateControls()
            tbIn = New TextBox
            With tbIn
                .Name = "tbIn"
                .Font = New Font("Arial", 12)
                .Text = "bitte Text eingeben"

                .SetBounds _
                        (10, 10, _
                         Me.ClientSize.Width - 20, _
                         .Height)

                .Anchor = _
                        AnchorStyles.Left Or _
                        AnchorStyles.Top Or _
                        AnchorStyles.Right

            End With
            Me.Controls.Add(tbIn)

            lblOut = New Label
            With lblOut
                .Name = "lblOut"
                .Font = tbIn.Font
                .AutoSize = True

                .Location = _
                    New Point(10, tbIn.Bottom + 20)

                .Text = "....."
            End With
            Me.Controls.Add(lblOut)
        End Sub

        Private Sub ShowText _
                (ByVal C As Control, _
                 ByVal Text As String)

            If C.InvokeRequired Then
                ' Aufruf erfolgte aus fremdem Thread
                Dim D As New DShowText(AddressOf ShowText)

                Dim PArray() As Object = _
                    New Object() {lblOut, Text}

                ' Sub ShowText erneut über den
                ' Delegaten DShowText aufrufen
                C.Invoke(D, PArray)
            Else
                ' Aufruf erfolgte aus dem Hauptthread
                C.Text = Text
            End If
        End Sub

        Private Sub ThreadProc()
            ' Diese Prozedur wird in einem zweiten Thread
            ' ausgeführt solange mUnloadFlag = False ist.
            ' Sub verlassen, wenn mUnloadFlag auf True
            ' gesetzt ist.

            Do While Not mUnloadFlag
                ShowText(lblOut, Now.ToLongTimeString)
                Thread.Sleep(3000)
            Loop
        End Sub

        Private Sub tbIn_TextChanged _
                (ByVal sender As Object, _
                 ByVal e As System.EventArgs _
                ) Handles tbIn.TextChanged

            If lblOut IsNot Nothing Then
                ShowText(lblOut, tbIn.Text)
            End If
        End Sub

    End Class
    ' \ \ \  E N T E

    Nach dem Programmstart siehst Du die Form mit einer
    Textbox und einem darunter angeordneten Label.

    Nach dem Programmstart wird auch gleich ein zweiter
    Thread (mTH) gestartet, der alle 3 Sekunden die akt.
    Uhrzeit in das Label schreibt.

    Wenn Du Text in die Textbox eingibst, wird dieser
    direkt im Label angezeigt. Dieser Zugriff auf das
    Label-Steuerelement (lblOut) erfolgt aus dem Hauptthread
    und kann somit direkt ohne Umweg erfolgen.

    Der Zugriff des zweiten Threads auf das Label-Steuerelement
    zum Anzeigen der Uhrzeit kann, weil threadübergreifender
    Zugriff, nicht direkt erfolgen sondern muss in der Sub ShowText
    im Abfragezweig

        if C.InvokeRequired then

    mit dem Hauptthread synchronisiert werden. Dies geschieht
    indem die Sub ShowText einfach über den Delegaten
    DShowText erneut aufgerufen wird.

    Wird die Form entladen, um das Programm zu beenden,
    wird in Form_Closing geprüft, ob der zweite Thread noch am
    Leben ist (mTH.IsAlive = True) und falls ja, muss diesem
    zweiten Thread mitgeteilt werden, dass er sich beenden
    soll  (mUnloadFlag = True) und solange gewartet
    werden (mTH.Join), bis dieser zweite Thread tatsächlich
    beendet ist Nachdem dies geschehen ist, wird die Forrm
    tatsächlich entladen und das Programm damit beendet.

    Gruß aus St.Georgen
    Peter Götz
    www.gssg.de (mit VB-Tipps u. Beispielprogrammen)

    Sonntag, 19. Dezember 2010 10:14
  • Zunächst einmal danke für die Tipps.

    Ein Test gestern mit der ganz einfahcen Variante hat gut funktioniert.

    Bin mal gespannt woran es jetzt als nächstes klemmt.

    Schonmal schöne Feiertage

    Montag, 20. Dezember 2010 14:08
  •     Private Sub Form1_FormClosing _

            (ByVal sender As Object, _
             ByVal e As FormClosingEventArgs _
            ) Handles Me.FormClosing

            ' fallse der zweite Thread noch aktiv ist, ...
            If mTH.IsAlive Then
                mUnloadFlag = True

                ' ... warten bis zweiter Thread beendet ist
                Console.WriteLine "Warten ..."
                mTH.Join()
                Console.WriteLine "... und weiter gehts."
            End If
        End Sub

    ...

        Private Sub ThreadProc()
            ' Diese Prozedur wird in einem zweiten Thread
            ' ausgeführt solange mUnloadFlag = False ist.
            ' Sub verlassen, wenn mUnloadFlag auf True
            ' gesetzt ist.

            Do While Not mUnloadFlag
                ShowText(lblOut, Now.ToLongTimeString)
                Thread.Sleep(3000)
            Loop
        End Sub


    Hallo,

    Ich habe/hatte ein ähnliches Problem und der Beitrag hat mir schonmal echt weitergeholfen. Danke.

    Eine Zusatzfrage: Gibt es eine schnellere Möglichkeit den zweiten Thread sauber zu beenden, wenn die Form geschlossen wird?

    Im zweiten Thread habe ich auch en Sleep und so wartet das Programm in diesem Fall drei Sekunden bis es schliesst, was etwas unschön ist.Es spielt mir keine Rolle, ob der Thread die Schleife noch fertig macht.

    Spricht etwas dagegen das so zu machen:

     

       Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As FormClosingEventArgs) Handles Me.FormClosing
    
     If t.IsAlive Then
       t.Abort()
      End If
    
    end sub

     

    Gruss

    Martin


    Freitag, 20. Mai 2011 11:23
  • Hallo Martin,

    ein Abort sollte man vermeiden, denn das ist vergleichbar mit dem Ziehen der Handbremse
    auf der Autobahn - und man weiss nicht, was dabei alles kaputt geht ;-)

    Besser Du verwendest einen Thread aus dem Thread-Pool via QueueUserItem.
    Die sind per defintionem als Hintergrund-Thread definiert und beenden sich selbsttätig.
    Oder eine BackgroundWorker-Komponente (die nutzt den ThreadPool indirekt)
    und bietet zugleich die Möglichkeit, Rückmeldungen an Dein Programm zu machen.

    Beim BackgroundWorker wiederum kannst Du über CancelAsync, Deinen Code zum sauberen
    Beenden auffordern. Der sollte dazu CancellationPending regelmässig prüfen.

    Ein Sleep in einem Hintergrundthread sollte man vermeiden.
    Beende lieber den alten und starte ggf. eher einen neuen
    (bei den oben beschrieben Verfahren ist das wenig aufwändig).

    Gruß Elmar

    P. S.: Bitte eröffne in Zukunft einen neuen Thread und beziehe ggf. auf ältere,
    das erhöht die Übersichtlichkeit.

    • Bearbeitet Elmar Boye Freitag, 20. Mai 2011 12:12 Sleep hinweis
    Freitag, 20. Mai 2011 12:11