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:
Image

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:
Image
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?