Passing data between Threads
Passing data between Threads
I'm making a network program and this is currently how I have my program planned out.
Main thread just handles the user interface and changes the gadgets to their values. It also has InitNetwork() in it, is that safe or do I have to call it in the thread that is using the Network Functions?
Now I have a listbox that displays the status messages like "Client Connected" or "Server Started on Port 545".
I understand that threads can't access gadgets in the main program so I've created a List of strings in the main thread so that the threads can access that and pass their messages over and the main thread will update the ListView when there are messages in the list.
Is this the best way to pass a string between threads? There really aren't many examples I could find.
Also with Mutexes, is it the programmer that sets what the mutex locks?
Like if I create a ListMutex variable can I create a different one for other lists?
If I don't check the same mutex then will that cause an error or a chance of both threads accessing the list?
Is it better to use two lists (one in the main program and one in the thread) because if the thread tries to lock the list mutex and add a message and the main window is frozen (like when it is being moved or the menu is being accessed) then the thread will lock while waiting or if I add a TryLockMutex it will run but it may lose the message if there is another.
Any help or examples of how you pass strings or other variables (to update gadgets or a progressbar) would be great.
Thanks
Main thread just handles the user interface and changes the gadgets to their values. It also has InitNetwork() in it, is that safe or do I have to call it in the thread that is using the Network Functions?
Now I have a listbox that displays the status messages like "Client Connected" or "Server Started on Port 545".
I understand that threads can't access gadgets in the main program so I've created a List of strings in the main thread so that the threads can access that and pass their messages over and the main thread will update the ListView when there are messages in the list.
Is this the best way to pass a string between threads? There really aren't many examples I could find.
Also with Mutexes, is it the programmer that sets what the mutex locks?
Like if I create a ListMutex variable can I create a different one for other lists?
If I don't check the same mutex then will that cause an error or a chance of both threads accessing the list?
Is it better to use two lists (one in the main program and one in the thread) because if the thread tries to lock the list mutex and add a message and the main window is frozen (like when it is being moved or the menu is being accessed) then the thread will lock while waiting or if I add a TryLockMutex it will run but it may lose the message if there is another.
Any help or examples of how you pass strings or other variables (to update gadgets or a progressbar) would be great.
Thanks

Re: Passing data between Threads
What's that?idle wrote:use a queue structure
Is it not the same as a List?

Re: Passing data between Threads
you can make a queue structure from list or an array
here's a generalised one
http://www.purebasic.fr/english/viewtop ... ilit=queue
here's a generalised one
http://www.purebasic.fr/english/viewtop ... ilit=queue
Windows 11, Manjaro, Raspberry Pi OS


- RichAlgeni
- Addict
- Posts: 935
- Joined: Wed Sep 22, 2010 1:50 am
- Location: Bradenton, FL
Re: Passing data between Threads
Call InitNetwork() in your main thread, it doesn't need to be called more than once.
Here's a quick breakdown of how I did what you're talking about. I have a maximum number of threads (numServers.i) that is read from an .ini file. I create a structure, then create a dimension-ed array using the structure, to the number of threads:
I then create events for the number of threads I will use #ProgramName is the constant used for the program name:
Then I create the threads needed:
For each thread, I have this code:
In the main procedure I check NetworkServerEvent() for #PB_NetworkEvent_Data, the Event Client ID is passed to the thread as thisThread(useThisOne) = thisClient.
I use the procedure checkEventClient(@thisClient) to see if the same client event ID as the previous event id has been returned. If it has, I ignore the event. Once a network read has been done, the client event id is reset. To end the process, I set the ShutDownNow variable to #Tue, then loop through the number of servers, and set each event. Each thread waits until it's event is triggered using SetEvent.
Take a look at the code, and let me know if you have any questions. Note that events are not only global, they are system wide, so they must be process specific, unless you need separate processes to be triggered from events, which I may use in the near future. The 'Break' is there because I use this code in a service, and I return to the service main loop after each Event is serviced, or no events have occurred. I hope this helps.
Rich
Here's a quick breakdown of how I did what you're talking about. I have a maximum number of threads (numServers.i) that is read from an .ini file. I create a structure, then create a dimension-ed array using the structure, to the number of threads:
Code: Select all
Structure socketRequest
socketClient.i
socketString.s
socketSource.s
paramFirst.s; whatever else you need...
EndStructure
Global Dim clientData.socketRequest(numServers)
Global ShutDownNow.i = #False
I then create events for the number of threads I will use #ProgramName is the constant used for the program name:
Code: Select all
Global Dim thisEvent.i(numServers)
For threadIndex=0 To numServers
thisEvent(threadIndex) = CreateEvent_(0,0,0, #ProgramName + "_Event_" + Str(threadIndex))
Next
Code: Select all
Global Dim thisThread.i(numServers)
For threadIndex=0 To numServers
thisThread(threadIndex) = 0
CreateThread(@ProcessRequest(), threadIndex)
Next
Code: Select all
Procedure.i ProcessRequest(requestNumber.i)
Repeat
WaitForSingleObject_(thisEvent(requestNumber), #INFINITE)
If ShutDownNow
Break
Else
thread code here....
Read data from the network socket into clientData.socketRequest(requestNumber), process as needed.
; tell our main loop we are done with this socket
thisThread(requestNumber) = 0
EndIf
ForEver
CloseHandle_(thisEvent(requestNumber))
EndProcedure
Code: Select all
Repeat
NSEvent = NetworkServerEvent() ; if we receive data, it will be indicated here
Select NSEvent
Case #PB_NetworkEvent_Connect; a socket has connected
Case #PB_NetworkEvent_Data; raw data has been received
thisClient = EventClient()
checkEventClient(@thisClient)
If thisClient > 0
Repeat
useThisOne = useThisOne + 1
If useThisOne > numServers
useThisOne = threadNumber
EndIf
If thisThread(useThisOne) = 0
thisThread(useThisOne) = thisClient
SetEvent_(thisEvent(useThisOne))
Break
EndIf
ForEver
Else
Delay(10)
EndIf
Case #PB_NetworkEvent_File; uploading a file
Case #PB_NetworkEvent_Disconnect; a socket disconnected
Default; if no data has been received, get out of the loop
Break
EndSelect
ForEver
Take a look at the code, and let me know if you have any questions. Note that events are not only global, they are system wide, so they must be process specific, unless you need separate processes to be triggered from events, which I may use in the near future. The 'Break' is there because I use this code in a service, and I return to the service main loop after each Event is serviced, or no events have occurred. I hope this helps.
Rich
Re: Passing data between Threads
Thanks for the post RichAlgeni (and idle), I've read through a few of your posts as they showed up about using network commands and threads.
I see some API in there which is what I'm ideally trying to avoid.
Managed to make the thread pass the messages over via two queues.
Just reading through your code and trying to understand it at the moment
I see some API in there which is what I'm ideally trying to avoid.
Managed to make the thread pass the messages over via two queues.
Just reading through your code and trying to understand it at the moment


- RichAlgeni
- Addict
- Posts: 935
- Joined: Wed Sep 22, 2010 1:50 am
- Location: Bradenton, FL
Re: Passing data between Threads
My way above is just one of a number of correct ways to accomplish what you want. If you are comfortable with your code, that's all that matters! But don't be afraid to experiment with other's code. That's how you learn.
If I may, I would suggest that you write small, tightly focused procedures, that have been tested thoroughly, and that you know work well. Put the procedures into include files (.pbi), and reuse them as often as possible. For instance, I have a procedure that reads from a socket, writes the data received to a passed memory address, or times out and returns. That's all it does. It sends back a positive number if data has been received. The procedure is also passed a timeout value, which forces the procedure to return a 0 if the timeout is exceeded. If a socket error occurs, a negative number is returned. That's all this procedure does. It doesn't care if there is an error, it doesn't handle errors. It just passes back a value. I have another procedure that writes data to a socket. That's all IT does. I know these procedures work, and have been debugged. I can store these procedure in a 'network code' include program file, include then into a mainline program, and know I will never have a problem with them. Keep using this type of method, and you will see that coding is often just including smaller bits of code you have already written, then filling in. You will write code quicker, and more accurately.
Rich
If I may, I would suggest that you write small, tightly focused procedures, that have been tested thoroughly, and that you know work well. Put the procedures into include files (.pbi), and reuse them as often as possible. For instance, I have a procedure that reads from a socket, writes the data received to a passed memory address, or times out and returns. That's all it does. It sends back a positive number if data has been received. The procedure is also passed a timeout value, which forces the procedure to return a 0 if the timeout is exceeded. If a socket error occurs, a negative number is returned. That's all this procedure does. It doesn't care if there is an error, it doesn't handle errors. It just passes back a value. I have another procedure that writes data to a socket. That's all IT does. I know these procedures work, and have been debugged. I can store these procedure in a 'network code' include program file, include then into a mainline program, and know I will never have a problem with them. Keep using this type of method, and you will see that coding is often just including smaller bits of code you have already written, then filling in. You will write code quicker, and more accurately.
Rich
Re: Passing data between Threads
I'll try to do that. I currently have two includes of my own commonly used functions and I'm adding more as I progress through the program 


Re: Passing data between Threads
Back to back sockets are still the best, unless you plan to use a mutex to lock a common queue/array etc.
you will have to check the source and destn thread on each message if you use a common queue.
The easiest seems to be back to back IP loopback sockets, or create your own "in buffer" per thread so that other threads can write to it. Thread locking in that case has to write the "thread id" of the thread sending the message if you need to know the source of the message. The other is always have a "in memory per thread rx/tx pair" but that is essentially what a socket does anyways (source ports are different)
one thing not found in x86 but in mircos is the JBC instruction, that jumps if a bit is set after reseting/clearing the bit
more like:
threadxlock=1 (1 indicates thread "x" buffer is free)
(this is thread incoming buffer lock)
thread1
JBC threadxlock, destn_Addr1
...
....
thread 2
JBC threadxlock, destn_Addr2
...
....
=====================================
The advantage being that the jump instruction branches to a different destn as well as resets the bit so if there is a context switch immediately, the other thread sees the bit as 0 immediately after the JBC instruction.
If I am not mistaken the IP sockets/unix sockets came about because of the coherency issues of not being able to send things across processes etc.
Why threads and why not processes using fork(), to me threads seem to do exactly what vfork() does in Unix/Linux...but that is best left to other folks to answer...vfork() does not copy the entire global space across processes....and am not sure if you can modify a global from the child in vfork()...and i think it suspends the parent.
you will have to check the source and destn thread on each message if you use a common queue.
The easiest seems to be back to back IP loopback sockets, or create your own "in buffer" per thread so that other threads can write to it. Thread locking in that case has to write the "thread id" of the thread sending the message if you need to know the source of the message. The other is always have a "in memory per thread rx/tx pair" but that is essentially what a socket does anyways (source ports are different)
one thing not found in x86 but in mircos is the JBC instruction, that jumps if a bit is set after reseting/clearing the bit
more like:
threadxlock=1 (1 indicates thread "x" buffer is free)
(this is thread incoming buffer lock)
thread1
JBC threadxlock, destn_Addr1
...
....
thread 2
JBC threadxlock, destn_Addr2
...
....
=====================================
The advantage being that the jump instruction branches to a different destn as well as resets the bit so if there is a context switch immediately, the other thread sees the bit as 0 immediately after the JBC instruction.
If I am not mistaken the IP sockets/unix sockets came about because of the coherency issues of not being able to send things across processes etc.
Why threads and why not processes using fork(), to me threads seem to do exactly what vfork() does in Unix/Linux...but that is best left to other folks to answer...vfork() does not copy the entire global space across processes....and am not sure if you can modify a global from the child in vfork()...and i think it suspends the parent.