-
Mar 18th, 2019, 09:50 AM
#1
Thread Starter
Addicted Member
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
-
Mar 18th, 2019, 10:06 AM
#2
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
-
Mar 18th, 2019, 04:42 PM
#3
Thread Starter
Addicted Member
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.
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.
-
Mar 18th, 2019, 05:57 PM
#4
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
-
Mar 19th, 2019, 04:55 PM
#5
Thread Starter
Addicted Member
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.
-
Mar 19th, 2019, 08:34 PM
#6
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
-
Mar 19th, 2019, 08:40 PM
#7
Thread Starter
Addicted Member
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
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|