Results 1 to 7 of 7

Thread: Help with cross thread error

  1. #1

    Thread Starter
    Addicted Member
    Join Date
    Nov 2013
    Posts
    196

    Help with cross thread error

    Newbie to threads and events and I am trying to work with a TCP Client example that was found on the web. There is a thread in it that needs to update a Form listbox (lbConnections). Doing that from the thread causes a cross thread error.

    I tried to resolve that problem by creating an event (NewConnection), a handler subroutine to update the listbox (UpdateDisplay), and then raise the event from inside the thread. Obviously that isn't the way to fix it or I am doing things wrong because the cross thread error still occurs from inside the handler sub. What am I doing wrong?

    Code:
        Event NewConnection(ByVal msg As String)
    
        Private Sub UpdateDisplay(ByVal msg As String) Handles Me.NewConnection
            lbConnections.Items.Add(msg)
        End Sub
    
        Public Sub listenerThread()
            AddHandler NewConnection, AddressOf UpdateDisplay
            Dim tcpListener As New TcpListener(IPHost.AddressList(1), 8080)
            Dim handlerSocket As Socket
            Dim thdstHandler As ThreadStart
            Dim thdHandler As Thread
            Dim update As New System.EventArgs
    
            tcpListener.Start()
            Do
                handlerSocket = tcpListener.AcceptSocket()
                If handlerSocket.Connected Then
                    RaiseEvent NewConnection(handlerSocket.RemoteEndPoint.ToString() + " connected.")
                    SyncLock (Me)
                        nSockets.Add(handlerSocket)
                    End SyncLock
                    thdstHandler = New ThreadStart(AddressOf handlerThread)
                    thdHandler = New Thread(thdstHandler)
                    thdHandler.Start()
                End If
            Loop
        End Sub

  2. #2
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    38,989

    Re: Help with cross thread error

    That's not a bad approach, just somewhat incorrect. The problem you are running into is that the thread will raise the event on the same thread, not on the UI thread. You can get it to post on the UI thread, which would solve the problem using the SynchronizationContext, and by other means.

    I was going to say that you might consider the BackgroundWorker, which can raise a ReportProgress event, which will be raised on the UI thread, but that doesn't really seem quite appropriate here. In general, a TCP listener usually makes sense as its own thing. A BGW component would be part of a form, and they tend to do things then quit. With TCP, you sit there listening, usually forever, with connections being accepted onto their own threads. That probably wouldn't be suitable (and may not even be really workable) for a BGW.

    So, consider this thread:

    http://www.vbforums.com/showthread.p...ass&highlight=

    In that, I am raising events on the UI thread from a background thread. Most of it you can ignore, except for this part from the very end of the snippet:

    Code:
    myContext.Post(AddressOf GotData, e)
        
    
        'This is invoked by the other thread.
        Private Sub GotData(ByVal state As Object)
            RaiseEvent MessageIn(Me, DirectCast(state, UDPEventArgs))
        End Sub
    myContext is the synchronizationContext. You can see where I set that much earlier. That's the UI thread context. I then Post to it, which means that it calls the GotData sub on the UI thread (while passing it an object for an argument, which can hold anything). That method just raises the event, which will be on the UI thread, but it could really do whatever you wanted at that point. I wanted the event, so raising it was all I did.
    My usual boring signature: Nothing

  3. #3

    Thread Starter
    Addicted Member
    Join Date
    Nov 2013
    Posts
    196

    Re: Help with cross thread error

    Making progress. The code below works (The First Time through) using the synchronizingContext technique you described. It gets an error at myContext.Post(AddressOf GotData, e).

    The error is System.NullReferenceException Object reference not set to an instance of an object.
    Source=TCP Server
    StackTrace:
    at TCP_Server.Form1.listenerThread() in C:\Users\Mike\Desktop\Visual Studio\TCP Server\TCP Server\Form1.vb:line 42
    at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
    at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
    at System.Threading.ThreadHelper.ThreadStart()

    At this point, I'm not sure if this is an error due to the way I included the synchronizationContext or something that was initially wrong in the example that I started with. This time I included the code in its entirety. The image shows the correct first results. The error then occurs when I connect to the server again.

    Name:  Clipboard01.jpg
Views: 127
Size:  6.4 KB Name:  Clipboard01.jpg
Views: 148
Size:  8.1 KB

    Code:
    Imports System.Threading
    Imports System.Net
    Imports System.Net.Sockets
    Imports System.Text
    Imports System.IO
    Imports TCP_Server.Form1
    Imports TCP_Server
    
    Public Class Form1
        Private nSockets As ArrayList
        Private Shared myContext As System.Threading.SynchronizationContext
        Public Event MessageIn(ByVal sender As Object, ByVal e As UDPEventArgs)
        Dim IPHost As IPHostEntry
    
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            IPHost = Dns.GetHostEntry(Dns.GetHostName())
            lblStatus.Text = "My IP address is " + IPHost.AddressList(1).ToString()
            nSockets = New ArrayList()
            Dim thdListener As New Thread(New ThreadStart(AddressOf listenerThread))
            'Synchronization.
            myContext = System.Threading.SynchronizationContext.Current
    
            thdListener.Start()
        End Sub
    
        Public Sub UIAction(Of T As Control)(ByVal Control As T, ByVal Action As Action(Of Control))
            'SyncContext.Send(New Threading.SendOrPostCallback(Sub() Action(Control)), Nothing)
        End Sub
    
        Public Sub listenerThread()
            Dim tcpListener As New TcpListener(IPHost.AddressList(1), 8080)
            Dim handlerSocket As Socket
            Dim thdstHandler As ThreadStart
            Dim thdHandler As Thread
            Dim update As New System.EventArgs
            Dim e As New UDPEventArgs
            tcpListener.Start()
            Do
                handlerSocket = tcpListener.AcceptSocket()
                If handlerSocket.Connected Then
                    e.Payload = handlerSocket.RemoteEndPoint.ToString() + " connected."
                    myContext.Post(AddressOf GotData, e)
                    SyncLock (Me)
                        nSockets.Add(handlerSocket)
                    End SyncLock
                    thdstHandler = New ThreadStart(AddressOf handlerThread)
                    thdHandler = New Thread(thdstHandler)
                    thdHandler.Start()
                End If
            Loop
        End Sub
    
        Public Sub handlerThread()
            Dim handlerSocket As Socket
            handlerSocket = nSockets(nSockets.Count - 1)
            Dim networkStream As NetworkStream = New NetworkStream(handlerSocket)
            Dim blockSize As Int16 = 1024
            Dim thisRead As Int16
            Dim dataByte(blockSize) As Byte
    
            myContext = System.Threading.SynchronizationContext.Current
    
            SyncLock Me
                ' Only one process can access the
                ' same file at any given time
                Dim fileStream As Stream
                fileStream = File.OpenWrite("c:\Users\Mike\Desktop\Output.txt")
                While (networkStream.DataAvailable)
                    thisRead = networkStream.Read(dataByte, 0, blockSize)
                    fileStream.Write(dataByte, 0, thisRead)
                    If thisRead < blockSize Then
                        Exit While
                    End If
                End While
                fileStream.Close()
            End SyncLock
            'lbConnections.Items.Add("File Written")
            handlerSocket = Nothing
        End Sub
    
        'This is invoked by the other thread.
        Private Sub GotData(ByVal state As Object)
            RaiseEvent MessageIn(Me, DirectCast(state, UDPEventArgs))
        End Sub
    
        Private Sub Form1_MessageIn(sender As Object, e As UDPEventArgs) Handles Me.MessageIn
            lbConnections.Items.Add(e.Payload)
        End Sub
    
        Public Class UDPEventArgs
            Inherits EventArgs
            Public Payload As String
        End Class
    End Class
    Last edited by PickyBiker; Mar 18th, 2019 at 04:46 PM.

  4. #4
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    38,989

    Re: Help with cross thread error

    That seems pretty odd, as there doesn't seem to be much on that line that COULD cause that exception. You can put a breakpoint on the line and take a look at each object. That exception happens when one is Nothing, but there isn't much that can be Nothing on that line.

    In more recent versions of VS, I've seen the exception misdirect you to the line AFTER it was actually thrown, too, so you might also look at the line before that. If e.payload is correct, then you know the exception isn't on that line, which would leave it on the line indicated.
    My usual boring signature: Nothing

  5. #5

    Thread Starter
    Addicted Member
    Join Date
    Nov 2013
    Posts
    196

    Re: Help with cross thread error

    The error is happening on the myContext.post line. I set a breakpoint and can see the first time through myContext has valid data. I pinned the value and watched it as I stepped through. That changes to "Nothing" as soon as the End if is executed. That means the second time through, I get the error.

    No clue why that is happening.

  6. #6
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    38,989

    Re: Help with cross thread error

    I have a suspicion, but not an answer. To test it out, you'll have to move the declaration of myContext into a module and out of the form. Having it as a shared member in the form should work, but it may be getting into a conflict with some slightly inconsistent behavior with how forms are handled relative to any other class within VB. So, move it into something that isn't a form. A module would be easiest.
    My usual boring signature: Nothing

  7. #7

    Thread Starter
    Addicted Member
    Join Date
    Nov 2013
    Posts
    196

    Re: Help with cross thread error

    Just tried it shaggy, sorry but it didn't help. It was a module.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width