Using threads, passing data back

Just starting out? Need help? Post your questions and find answers here.
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Using threads, passing data back

Post by Oso »

Hello all, I am trying to get to know the capabilities of threads, with the intention of implementing threading in existing code. In the documentation https://www.purebasic.com/documentation ... hread.html it makes clear that passing values back is not supported. It confirms, "If you do try to return a value from your thread it will simply be lost". Do we have any other existing options for returning data?

From what I can see, passing parameters to a threaded procedure is done through a structure. In the sample code on the linked page, it shows a person's name, age and phone number being passed in that way. I just wanted to check if this is the only way, before I begin making changes.

Also what are the limits in terms of numbers of concurrent threads? Is this a function of the processor? In the below AMD specs, it refers to number of threads. I'm not sure if this is the same thing, although I know that cores refers to processors. Thanks for any information on this.

Code: Select all

AMD Ryzen 5 7600X      AMD Ryzen 7 7700X      AMD Ryzen 9 7900X
Cores 6, Threads 12    Cores 8, Threads 16    Cores 12, Threads 24
Last edited by Oso on Fri Dec 23, 2022 7:16 am, edited 1 time in total.
User avatar
NicTheQuick
Addict
Addict
Posts: 1510
Joined: Sun Jun 22, 2003 7:43 pm
Location: Germany, Saarbrücken
Contact:

Re: Using threads, passing data back

Post by NicTheQuick »

Your operating system keeps track of all the threads which are running currently on your system. There is an upper limit but you have to try hard to max it out. On my system for example there are currently running 3568 threads. It has nothing to to with the thread count a given processor is able to run in parallel. If a processor has a high thread count it just can run more threads in parallel at any time and such makes your system run faster.

If you want to be able to return values from a thread, you can use the structure which you are passing to the thread. I did something like this in the past but I can not find the topic for that again here in the forum. In other programming languages it often is called "joining threads" because after running concurrently for a while they join the main thread again. You can achieve something like this with WaitThread and a structure.
The english grammar is freeware, you can use it freely - But it's not Open Source, i.e. you can not change it or publish it in altered way.
User avatar
NicTheQuick
Addict
Addict
Posts: 1510
Joined: Sun Jun 22, 2003 7:43 pm
Location: Germany, Saarbrücken
Contact:

Re: Using threads, passing data back

Post by NicTheQuick »

I just wrote something for you:

Code: Select all

Prototype.i ThreadProc(parameter.i)

Structure ThreadInfo
	threadid.i
	threadproc.ThreadProc
	parameter.i
	returnValue.i
EndStructure

Procedure WrapperThread(*threadInfo.ThreadInfo)
	*threadInfo\returnValue = *threadInfo\threadproc(*threadInfo\parameter)
EndProcedure

Procedure.i JoinThread(*threadInfo.ThreadInfo)
	WaitThread(*threadInfo\threadid)
	ProcedureReturn *threadInfo\returnValue
EndProcedure

Procedure.i MyCreateThread(*threadProc, parameter.i)
	Protected *threadInfo.ThreadInfo = AllocateStructure(ThreadInfo)
	*threadInfo\parameter = parameter
	*threadInfo\threadproc = *threadProc
	*threadInfo\threadid = CreateThread(@WrapperThread(), *threadInfo)
	ProcedureReturn *threadInfo
EndProcedure


; Here begins your program

Procedure mythread(parameter)
	; Sum up all values up to parameter
	Protected sum.i, i.i
	For i = 1 To parameter
		sum + i
		; Make it run slower
		Delay(1000)
	Next
	ProcedureReturn sum
EndProcedure


Debug "Start thread"
Define threadId.i = MyCreateThread(@mythread(), 3)

Debug JoinThread(threadId)
Hope that helps.
The english grammar is freeware, you can use it freely - But it's not Open Source, i.e. you can not change it or publish it in altered way.
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Using threads, passing data back

Post by Oso »

NicTheQuick wrote: Thu Dec 22, 2022 5:15 pm I just wrote something for you:
Hope that helps.
Many thanks NicTheQuick, I appreciate the explanation about parallel CPU threads. I'm struggling to follow what the example does though (it's getting late here so perhaps in the morning I will follow it better :D ). Is the complexity of having those four procedures necessary in order to wait for the thread to finish? Also, do you need to free the structure? The example in the documentation frees the allocated memory inside the thread, but I realise of course we can't do that in this case because we're using it to pass data back.

I'm confused by the line ProcedureReturn sum because the documentation suggests returned values are lost.
User avatar
skywalk
Addict
Addict
Posts: 4212
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Using threads, passing data back

Post by skywalk »

Look up Semaphore. I use them to signal threads to pause when writing to a shared data structure.
infratec and mk-soft have excellent threading examples.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
NicTheQuick
Addict
Addict
Posts: 1510
Joined: Sun Jun 22, 2003 7:43 pm
Location: Germany, Saarbrücken
Contact:

Re: Using threads, passing data back

Post by NicTheQuick »

You are right. In the JoinThread procedure you should use FreeStructure() to deallocate the memory used for that construction.

> I'm confused by the line ProcedureReturn sum because the documentation suggests returned values are lost.

That works because the real thread is always WrapperThread() which catches that return value.
The english grammar is freeware, you can use it freely - But it's not Open Source, i.e. you can not change it or publish it in altered way.
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Using threads, passing data back

Post by Oso »

Many thanks indeed for the replies NicTheQuick and skywalk, I think it's sensible that I first spend some time using threads and become familiar through trying it.
skywalk wrote: Thu Dec 22, 2022 10:17 pm Look up Semaphore. I use them to signal threads to pause when writing to a shared data structure. infratec and mk-soft have excellent threading examples.
Yes, I saw mutex and locking when I started reading about threads and I guess semaphores is related to that. This looks ideal for what I need.
NicTheQuick wrote: Thu Dec 22, 2022 11:11 pm That works because the real thread is always WrapperThread() which catches that return value.
Okay, got that NicTheQuick, thanks very much. I'm working on some simple examples to ease myself into this further.
Marc56us
Addict
Addict
Posts: 1600
Joined: Sat Feb 08, 2014 3:26 pm

Re: Using threads, passing data back

Post by Marc56us »

Hi Oso,

Read carefully what threads are in PB. It is not the same as in other languages.
Threads are useful for long operations. It is better to do as little as possible.
The more threads you use in a program, the more difficult it becomes to manage the whole thing in a safe way. You always end up forgetting to close some of them (fortunately closing the main program closes them all, but sometimes it forces the user to quit and restart the program)

See Help PureBasic - Thread - Overview

A thread is a part of a program which runs asynchronously, in the background of this program. This means it's possible to perform long operations (compression, image processing, etc) without halting the whole program and let the user continue to do other things. A thread runs within your program, it's not another process. When the main program exits, all the threads are destroyed. Under PureBasic, threads are simply a procedure which is called asynchronously. The thread runs until the procedure exits.

Examples of places of programs where threads are useful are when you need to be able to handle multiple situations with different response times or which occur at different intervals. In the above paragraph, the response times of image processing and the user interface are quite different (you would want to wait for the image to be processed but always have the user interface to respond).
[...]

:wink:
User avatar
skywalk
Addict
Addict
Posts: 4212
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Using threads, passing data back

Post by skywalk »

I disagree. I think everyone should be using threads and understand them.
When I see an app with an hourglass cursor I get annoyed. Much more professional to show a progress bar.

I will say that debugging a threaded app is more painful and the debugger sometimes gets lost in the main GUI event loop. Some breakpoints in the threads are missed. Stuff like that.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Using threads, passing data back

Post by Oso »

Thanks for the replies Marc56us and skywalk I'd hoped to look at this today but still behind with work and the holiday preparations too. We all have different requirements in regards to what we are developing. I feel that threads can become complex and it isn't always best to create a complex solution that becomes difficult for others to maintain. I come from a legacy background where there is no multithreading anyway, but more recently I've been working in C#, which has an asynchronous method. It is encouraged that you should hand over processing to a thread, but wait for it to complete, hence the below 'await'. This is done so that the event-driven UI on the main thread doesn't become unresponsive, which is your point I think, skywalk.

Code: Select all

static async Task Main(string[] args)
{
    Egg eggs = await FryEggsAsync(2);
    Console.WriteLine("eggs are ready");

    Bacon bacon = await FryBaconAsync(3);
    Console.WriteLine("bacon is ready");
}
In my case I'm writing server software, so it isn't a desktop application with an hourglass — nobody will see the work that the server process is doing, but they might find that the client end that makes requests to the server is sometimes slow, and it's for that reason I aim to use threading at the server side. At present I have a remarkably fast server process, but it serves only a single user's request at a time, so my objective is that when it receives a request, it instead hands that over to a thread and then continues to work for other incoming requests. So threads seem absolutely perfect for this (fingers crossed :D )
Last edited by Oso on Sat Dec 24, 2022 7:18 am, edited 1 time in total.
User avatar
mk-soft
Always Here
Always Here
Posts: 6222
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Using threads, passing data back

Post by mk-soft »

Everything that belongs to serial or network communication should also be outsourced to threads and so that the GUI does not block or the GUI does not block the communication. Large pre-processing of data should also be outsourced to threads.
However, the transfer of data to the GUI should always take place in the main program, as this would otherwise lead to a crash under macOS and Linux and could also hang under Windows.
In order to send a message from the thread to the GUI main programme, there is an extra function called 'PostEvent'.

Reminder: Mini Thread Control
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Using threads, passing data back

Post by Oso »

mk-soft wrote: Fri Dec 23, 2022 9:11 pm Everything that belongs to serial or network communication should also be outsourced to threads and so that the GUI does not block or the GUI does not block the communication. Large pre-processing of data should also be outsourced to threads.
Thanks mk-soft it's beginning to fall into place now. I think it seems fairly logical which processes are candidates for threads. In particular, procedures that don't need to return any value (but just carry out a file update for example) are ideal.
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Using threads, passing data back

Post by Oso »

I found that I can pass values back from a thread using global variables (including global arrays). I don't expect this will be a revelation to others who use threads, because I'm sure you all know this already, but I just found it by chance. :D In my example below, testglobal.s(10) is set to a new value and that element is available in the calling main process.

I wondered if the thread can access its own thread id. If the thread knows its own thread id., then it could pass a value via the appropriate array element — i.e. thread number 1 = element number 1. I don't know if there is a function to return the thread id. inside a thread. Perhaps others will know the answer.

Even if that is not possible, it is easy to pass a sequential count parameter to the thread and let the thread set that particular array element.

Code: Select all

EnableExplicit
OpenConsole()
Global Dim testglobal.s(100)

Procedure Thread(*Dummy)
  testglobal.s(10) = "New string set inside thread"
EndProcedure

Define *Dummy
Define threadid1 = CreateThread(@Thread(), *Dummy)

PrintN("Thread id. " + Str(threadid1) + " started")
PrintN("")
If threadid1
  WaitThread(threadid1)
EndIf

PrintN("Thread has set testglobal.s(10) to : " + testglobal.s(10))

Print("Finished - Press Enter : ")
Input()
User avatar
Caronte3D
Addict
Addict
Posts: 1359
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: Using threads, passing data back

Post by Caronte3D »

To be able to use global variables in threads you must use some lock system to read and write to it.
I ever use LockMutex() but you can use semaphores or your own lock method
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Using threads, passing data back

Post by Oso »

Caronte3D wrote: Sat Dec 24, 2022 10:24 am To be able to use global variables in threads you must use some lock system to read and write to it.
I ever use LockMutex() but you can use semaphores or your own lock method
Ah! I thought it seemed to good to be true, Caronte3D :D Thanks for mentioning that little caveat. Yeah, it makes sense though, presumably because another process could be changing the contents of the global variable.
Post Reply