Page 1 of 2
Thread Parameters?
Posted: Tue Apr 16, 2024 9:14 pm
by Maya
Hi,
Just a question, is there a way to use more than one parameter in the "CreateThread" function?
Something like:
Code: Select all
CreateThread(@Thread(), Parm1, Parm2, Parm3)
Re: Thread Parameters?
Posted: Tue Apr 16, 2024 9:18 pm
by mk-soft
Code: Select all
Structure udtParameters
Parm1.i
Parm2.i
Parm3.i
EndStructure
Global myParams.udtParameters
myParams\Parm1 = 1
myParams\Parm2 = 3
myParams\Parm3 = 5
Procedure Thread(*myParams.udtParameters)
Protected a
a = *myParams\Parm1
Debug a
EndProcedure
CreateThread(@Thread(), @myParams)
Delay(1000)
More over threads see
https://www.purebasic.fr/english/viewtopic.php?t=73231
Re: Thread Parameters?
Posted: Wed Apr 17, 2024 8:22 pm
by Maya
Thank you mk-soft, however I'm still have a problem when applying your code.
Let's say we need to convert the following simpl Message box procedure into Thread:
Code: Select all
ProcedureDLL Msg (Title.s, Num1.i, Num2.i)
MessageRequester (Title, Str(Num1 + Num2))
EndProcedure
Msg ("Welcome", 100, 100)
To:
Code: Select all
ProcedureDLL Thread (*myParams.Parameters)
MessageRequester ("Title", Str(Num1+Num2))
EndProcedure
Structure Parameters
Title.s
Num1.i
Num2.i
EndStructure
Global myParams.Parameters
myParams\Title = "Title"
myParams\Num1 = 100
myParams\Num2 = 200
CreateThread (@Thread(), @myParams)
Delay(2500)
The result error appears as follows:
Any help will be much appreciated.
Re: Thread Parameters?
Posted: Wed Apr 17, 2024 8:37 pm
by Demivec
Maya wrote: Wed Apr 17, 2024 8:22 pm
Thank you mk-soft, however I'm still have a problem when applying your code.
The result error appears as follows:
You need to place the structure definition before the thread procedure and to also reference the Parameters (in the structure that was passed as a pointer) like so:
Code: Select all
Structure Parameters
Title.s
Num1.i
Num2.i
EndStructure
ProcedureDLL Thread (*myParams.Parameters)
MessageRequester ((*myParams\Title, Str((*myParams\Num1+(*myParams\Num2))
EndProcedure
Global myParams.Parameters
myParams\Title = "Title"
myParams\Num1 = 100
myParams\Num2 = 200
CreateThread (@Thread(), @myParams)
Delay(2500)
Re: Thread Parameters?
Posted: Thu Apr 18, 2024 12:33 am
by BarryG
A simpler alternative if speed isn't an issue:
Code: Select all
; Enable thread safety in Compiler Options!
Procedure MyThread(params)
text$=PeekS(params)
n=CountString(text$,",")+1
For i=1 To n
p$=StringField(text$,i,",")
If p$
Debug p$
EndIf
Next
EndProcedure
text$="1,2,3,4,5" ; Five comma-separated params.
CreateThread(@MyThread(),@text$)
Delay(100) ; So PeekS() in MyThread() has time to copy the params.
Re: Thread Parameters?
Posted: Thu Apr 18, 2024 6:15 am
by STARGÅTE
At this point I would like to give an important note for both proposed solutions.
In both cases the information is passed just as a reference to the thread, and not as a variable or copy.
This means, the myParams.Parameters (first example) or text$ (second example) have to keep as they are as long as the thread is executed.
Re: Thread Parameters?
Posted: Thu Apr 18, 2024 8:18 pm
by mk-soft
STARGÅTE wrote: Thu Apr 18, 2024 6:15 am
At this point I would like to give an important note for both proposed solutions.
In both cases the information is passed just as
a reference to the thread, and
not as a variable or copy.
This means, the myParams.Parameters (first example) or text$ (second example) have to keep as they are as long as the thread is executed.
Thank you for mentioning them.
Sometimes I assume that this is known
Re: Thread Parameters?
Posted: Thu Apr 18, 2024 10:09 pm
by BarryG
STARGÅTE wrote: Thu Apr 18, 2024 6:15 amIn both cases the information is passed just as
a reference to the thread, and
not as a variable or copy.
That's why my example has "text$=PeekS(params)" as the first line of the thread: to create a local copy of the params so they're not lost. Sometimes you'll need a small delay after creating the thread (100ms works great) so that PeekS() has time to copy the data. Edited my other post to show this.
Re: Thread Parameters?
Posted: Thu Apr 18, 2024 11:15 pm
by mk-soft
It is always better to take a structure and create it for the entire runtime of the thread with AllocateStructure.
Re: Thread Parameters?
Posted: Fri Apr 19, 2024 7:49 am
by STARGÅTE
BarryG wrote: Thu Apr 18, 2024 10:09 pm
STARGÅTE wrote: Thu Apr 18, 2024 6:15 amIn both cases the information is passed just as
a reference to the thread, and
not as a variable or copy.
That's why my example has "text$=PeekS(params)" as the first line of the thread: to create a local copy of the params so they're not lost. Sometimes you'll need a small delay after creating the thread (100ms works great) so that PeekS() has time to copy the data. Edited my other post to show this.
It's good that you're aware of this. However, such solution with Delay and the hope PeekS() has enough time is not optimal.
The use of a Semaphore would be more elegant and safe:
Code: Select all
; Enable thread safety in Compiler Options!
Global Semaphore.i = CreateSemaphore()
Procedure MyThread(params)
text$=PeekS(params)
SignalSemaphore(Semaphore)
n=CountString(text$,",")+1
For i=1 To n
p$=StringField(text$,i,",")
If p$
Debug p$
EndIf
Next
EndProcedure
text$="1,2,3,4,5" ; Five comma-separated params.
CreateThread(@MyThread(),@text$)
WaitSemaphore(Semaphore)
text$="11,12,13,14,15" ; Five comma-separated params.
CreateThread(@MyThread(),@text$)
WaitSemaphore(Semaphore)
Delay(100)
Re: Thread Parameters?
Posted: Fri Apr 19, 2024 11:18 am
by BarryG
@STARGÅTE: Thanks for showing that. I should switch my Delays() to Semaphores() then for my threads? But, can I just use the one semaphore for all my different threads, or does each thread need its own dedicated semaphore?
What I mean is, is doing it like this safe? It
appears to be.
Code: Select all
; Enable thread safety in Compiler Options!
Global Semaphore.i = CreateSemaphore()
Procedure MyThread_One(params)
text$=PeekS(params)
SignalSemaphore(Semaphore)
n=CountString(text$,",")+1
For i=1 To n
p$=StringField(text$,i,",")
If p$
Debug p$
EndIf
Next
EndProcedure
Procedure MyThread_Two(params)
text$=PeekS(params)
SignalSemaphore(Semaphore)
n=CountString(text$,",")+1
For i=1 To n
p$=StringField(text$,i,",")
If p$
Debug p$
EndIf
Next
EndProcedure
text$="1,2,3,4,5" ; Five comma-separated params.
CreateThread(@MyThread_One(),@text$)
WaitSemaphore(Semaphore)
text$="11,12,13,14,15" ; Five comma-separated params.
CreateThread(@MyThread_Two(),@text$)
WaitSemaphore(Semaphore)
Delay(100)
Re: Thread Parameters?
Posted: Fri Apr 19, 2024 12:10 pm
by NicTheQuick
I once wrote this:
https://www.purebasic.fr/english/viewto ... 39#p592739
And now I updated the code a bit to also include multiple parameters. Maybe that's interesting for you:
Code: Select all
EnableExplicit
Prototype.i ThreadProc(parameter.i)
Structure ThreadInfo
threadid.i
mutex.i
threadproc.ThreadProc
returnValue.i
EndStructure
Procedure WrapperThread(*threadInfo.ThreadInfo)
With *threadInfo
LockMutex(\mutex)
\returnValue = \threadproc(*threadInfo)
UnlockMutex(\mutex)
EndWith
EndProcedure
Procedure.i JoinThread(*threadInfo.ThreadInfo)
Protected returnValue.i
With *threadInfo
WaitThread(\threadid)
returnValue = \returnValue
FreeMutex(\mutex)
FreeStructure(*threadInfo)
EndWith
ProcedureReturn returnValue
EndProcedure
Procedure.i CreateThreadEx_(*threadProc, *threadInfo.ThreadInfo)
If Not *threadInfo
ProcedureReturn #False
EndIf
With *threadInfo
\threadproc = *threadProc
\mutex = CreateMutex()
\threadid = 0
LockMutex(\mutex)
EndWith
ProcedureReturn *threadInfo
EndProcedure
Macro CreateThreadEx(threadProc, structureName)
CreateThreadEx_(threadProc, AllocateStructure(structureName))
EndMacro
Procedure StartThread(*threadInfo.ThreadInfo)
With *threadInfo
If Not \threadid
\threadid = CreateThread(@WrapperThread(), *threadInfo)
UnlockMutex(\mutex)
EndIf
EndWith
EndProcedure
; ========================
; Here begins your program
; ========================
; Define your custom structure with all the parameters your thread needs
Structure mythread Extends ThreadInfo
parameter1.i
parameter2.i
parameter3.s
EndStructure
; Define your cthread
Procedure mythread(*mythread.mythread)
Protected sum.i, i.i, j.i
With *mythread
; Do something silly
For i = 1 To \parameter1
For j = 1 To \parameter2
sum + j
Debug \parameter3 + " sum: " + sum
; Make it run slower
Delay(50)
Next
Next
EndWith
; Return sum
ProcedureReturn sum
EndProcedure
; Create the thread and define its parameters
Debug "Define thread"
Define *thread1.mythread = CreateThreadEx(@mythread(), mythread)
*thread1\parameter1 = 10
*thread1\parameter2 = 2
*thread1\parameter3 = "Test"
Debug "Start thread"
StartThread(*thread1)
Debug "New threadid: " + *thread1\threadid
Debug "Join thread and get its return value"
Debug "Sum: " + Str(JoinThread(*thread1))
Re: Thread Parameters?
Posted: Fri Apr 19, 2024 2:07 pm
by STARGÅTE
BarryG wrote: Fri Apr 19, 2024 11:18 am
@STARGÅTE: Thanks for showing that. I should switch my Delays() to Semaphores() then for my threads? But, can I just use the one semaphore for all my different threads, or does each thread need its own dedicated semaphore?
What I mean is, is doing it like this safe? It
appears to be.
Semaphores guarantee that the thread has read the passed data completely.
A delay pauses the main program either to long or to short, not practicable.
So yes, I would recommend to switch to Semaphore.
Actually, the semaphore is used here not for the individual threads, it is used for the shared string parameter which you use. Therefore
one semaphore is enough when you want to protect
one variable.
Re: Thread Parameters?
Posted: Fri Apr 19, 2024 3:26 pm
by skywalk
Yes, semaphores have been very reliable for me.
Just be careful with the shared use of Map() data.
This is my NOTE TO SELF:
; Current PB Design means a Map()'s current element pointer is NOT "threaded".
; A read in one thread can change the current element within another thread.
; Unlike a shared Array() where each thread can browse different indices,
; if a current map element changes, then another thread will be tricked with its request for next map element.
; FEATURE REQUEST: Allow Map() read access from multiple threads.
Re: Thread Parameters?
Posted: Sat Apr 20, 2024 11:00 am
by BarryG
The Semaphore approach isn't going to work for me, because "Global Semaphore.i = CreateSemaphore()" is global and my threads are going to be called more than once, so a global semaphore for a thread is going to send the signal to the wrong thread callers, right?