It’s late and the OOP side of my brain is failing me. I create a custom thread class and a custom tcp socket class in the IDE. A method in the window open event instantiates the thread in code assigning it to a property and a method in the thread instantiates the socket in code assigning that to a property. How do I get code in an event the in the socket to address methods and properties in the thread once they’ve been instantiated? Brain freeze, help !!
Have the thread pass itself to the socket, ideally in the constructor.
theSocket = new MyTCPSocketClass(me)
However, I don’t think you gain much having the thread instantiate a socket, since they’re pretty much independent anyway - the socket’s events all occur on the main thread, not in your thread. It might be better to have the window instantiate both the thread and the socket and act as the go-between.
[quote=63324:@Tim Hare]Have the thread pass itself to the socket, ideally in the constructor.
theSocket = new MyTCPSocketClass(me)
However, I don’t think you gain much having the thread instantiate a socket, since they’re pretty much independent anyway - the socket’s events all occur on the main thread, not in your thread. It might be better to have the window instantiate both the thread and the socket and act as the go-between.[/quote]
This is not correct. They do not necessarily happen on the main thread. I use threads and sockets extensively and socket events absolutely happen in threads for the most part - particularly writes and so forth. Asynchronous events like DataAvailable may happen on the main thread but they may not. I’ve seen both for the same piece of code. It all depends on how the event gets fired. If you call socket.poll from your thread, then the DataAvailable event fires in the thread. If you let it fire as the data arrives, it happens on the main thread.
So create methods for all the events that you want to handle for the socket. Set up AddHandler directives to use those methods to handle the events. Set up the AddHandler as the thread starts if the socket is local to the thread and will be destroyed once the thread is gone. Otherwise, set up the AddHandler in the main thread so you don’t call it each time the thread runs and you’ll get errors.
Jon could you post a code example from your two posts? Thank you very much.
Well, for example…
I have an object in code that represents a real world device. I connect to that device via a TCP socket that is a property of that object. So in my constructor method I have;
TCPSock = New TCPSocketPlus
AddHandler TCPSock.DataAvailable, AddressOf TCPDataAvailable
AddHandler TCPSock.Error, AddressOf TCPSockErrorEvent
AuxSocket = New TCPSocketPlus
AddHandler AuxSocket.Error, AddressOf TCPSockErrorEvent
TCPSocketPlus is a custom subclass of TCPSocket.
Now, for my Methods used:
Sub TCPDataAvailable(t as TCPSocket)
If t.Lookahead(Encodings.UTF8).InStr("login") <> 0 Then
If Not DidLogin Then
Dim junque as String = t.ReadAll
Write("root"+EndOfLine)
DidLogin = True
Connected = True
'System.Log(System.LogLevelNotice,"Did the device login")
End If
End If
If LoggedIn = False And t.Lookahead(Encodings.UTF8).InStr("BusyBox") <> 0 Then
LoggedIn = True
End If
If WorkToDo = WorkTasks.ParseData Then
If Not Queued Then
ThreadPoolManager.Queue(me)
Queued = True
End If
End If
End Sub
And
Sub TCPSockErrorEvent(t as TCPSocketPlus)
If Not t.SockConnected or t.LastErrorCode <> 0 Then
Connected = False
TCPSock.SockConnected = False
AuxSocket.SockConnected = False
TCPSock.Close
AuxSocket.Close
RaiseEvent BoxDisconnected
If ReconnectTimer.Mode = Timer.ModeOff Then
ReConnectTimer.Mode = Timer.ModeSingle
'ReConnectTimer.Reset
End If
End If
End Sub
Now, as far as calling a socket from a thread, I don’t think I necessarily need to show you how to do that. But there I’ve opened my kimono and showed you how to use methods as event handlers.
Now for threading, I have an class interface defined for my object where I send the entire object to my ThreadPoolManager. The ThreadPoolManager calls a method of the class interface called “DoWork.” In DoWork, I decide what I want to do in the thread based on the Enum value of WorkToDo. In my DoWork method I have a Select Case Statement that branches based on the Enum. You can see up there in the code where I set a value. So for example, WorkTasks.ParseData is what I use to parse the initial data and parameters of the device I connect to upon login. All that is done in a thread and all of it is written in a thread. I limit my ThreadPoolManager to 15 active threads. Any additional objects sent to the manager are queued and wait until a thread becomes available. I have Aaron Ballman I believe to thank for that piece of code (it’s either him or Alex Restreppo - I forget which).
Now, in another piece of code, I have the following for the run event of a thread in my app. This thread doesn’t use the ThreadPoolManager. All this is done in the thread with the socket write’s happening in a thread. I know they are threaded because all the writes happen in the thread.
Sub GrabThreadRun(th as thread)
#pragma Unused th
Do
SourceDevice.AuxSocket.Write("SOME_COMMAND"+EndOfLine)
SourceDevice.AuxSocket.Write("SOME_OTHER_COMMAND"+EndofLine)
app.SleepCurrentThread(SleepTime)
Loop
End Sub
In the DataAvailable event for the socket I have this:
Sub GrabSocketDataAvailable(t as TCPSocket)
If Draw Then
Dim s as String
Dim p as Picture
s = t.Lookahead
p = BMPStringtoPictureMBS(s)
If p <> Nil Then
PicArray.Append p
me.Refresh
s = t.Readall
t.listen
End If
End If
End Sub
Now, this runs on the main thread. The “Me” object here is a canvas. I can refresh the canvas and it works just fine on the main thread because I let the DataAvailable event fire on the main thread. If I were to call
SourceDevice.AuxSocket.Poll
from the Thread above, the DataAvailable event would be called from within the thread and I would get a “ThreadAccessUIException” thrown. Trust me. I’ve done it and confirmed it multiple times.
Did this help?
Crap. Wrong Tags put into the editor. Needed “[” instead of “<” and can’t edit posts which is a PITA.
Yes Joe thank you very much!!
I haven’t had a chance to read the rest of a thread, but here’s how sockets work:
- The main thread’s event loop services each socket and fires events as needed. So, without having to call poll, sockets will work and their events will fire on the main thread.
- User code calls Poll on a socket. This can fire events for that socket on whatever thread Poll got invoked on.
There usually shouldn’t be a need to call Poll, as long as your main event loop isn’t blocked (which it shouldn’t be because that just provides a bad user experience).
If you’re Polling from the thread, isn’t there some chance that the main thread will also poll the socket? You really can’t be sure which thread the event will eventually fire in, so don’t count on it. Unless you block the main thread while you’re polling via a pragma, but doesn’t that defeat the purpose of the thread?
No, not at all. It is possible that the main thread will poll the socket, but likely if your thread is running, it will invoke it first if you so choose to do it that way. However, having code in DataAvailable is not necessary. You can always in a thread use lookahead to see if there is data in the buffer. Then you can process that data in the thread. So there’s a lot of ways to skin the cat here.
[quote=63410:@Joe Ranieri]
There usually shouldn’t be a need to call Poll, as long as your main event loop isn’t blocked (which it shouldn’t be because that just provides a bad user experience).[/quote]
Actually, it’s many times faster to call poll in order to force the DataAvailable event to fire. Additionally, if you are doing a connect loop like:
Socket.Connect
Do Until Socket.IsConnected
Socket.Poll
Loop
That allows you to run a nice loop to be sure the socket connects before moving on.
I would like to do tcp socket in a thread not for main thread. main window holds all is better .
[quote=63601:@Jon Ogden]Socket.Connect
Do Until Socket.IsConnected
Socket.Poll
Loop[/quote]
That’s a tight loop that will block the event loop so poll is required. Too bad about any UI (or any other thread) activity.
I use the connected event to trigger processing, and a timer (which is cancelled by the connected event handler) to trigger a retry or initiate a connection failed process.