Thread Parameters?

Just starting out? Need help? Post your questions and find answers here.
AZJIO
Addict
Addict
Posts: 2225
Joined: Sun May 14, 2017 1:48 am

Re: Thread Parameters?

Post by AZJIO »

BarryG wrote: Sat Apr 20, 2024 11:00 am 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?
Semaphore is like a flag, you can create them separately for each function and activate your own semaphore within each thread. Don't you see inside the thread the activation of the semaphore signal specified by a specific variable.
Create an array of semaphores and count the number of thread calls and for each instance check its semaphore.
BarryG
Addict
Addict
Posts: 4219
Joined: Thu Apr 18, 2019 8:17 am

Re: Thread Parameters?

Post by BarryG »

Okay, this seems to be working for me in the way I need it. I can't see anything wrong with this approach? The goal is to show the time and date, from different threads, each time the button is clicked. Having one single global semaphore doesn't seem to be a problem? I'm not overlooking anything? Seems to work as expected when I keep clicking the button, because the "T1" and "T2" lines in the debug output are always toggling, ie. there's no T1 twice in a row or T2 twice in a row.

Code: Select all

; Enable thread-safety in Compiler Options.

Global param$
Global semaphore=CreateSemaphore()

Procedure MyThread_One(param)
  time$=PeekS(param)
  SignalSemaphore(semaphore)
  Debug "T1 -> "+time$ ; Should show the time.
  Sleep_(60000)
EndProcedure

Procedure MyThread_Two(param)
  date$=PeekS(param)
  SignalSemaphore(semaphore)
  Debug "T2 -> "+date$ ; Should show the date.
  Sleep_(60000)
EndProcedure

OpenWindow(0,400,200,170,80,"test",#PB_Window_SystemMenu)
ButtonGadget(0,20,20,130,30,"Click this repeatedly")

Repeat

  ev=WaitWindowEvent()

  If ev=#PB_Event_Gadget

    p1$=FormatDate("%hh:%ii:%ss",Date())
    CreateThread(@MyThread_One(),@p1$)
    WaitSemaphore(semaphore)
    p1$="" ; Required here.

    p2$=FormatDate("%yyyy/%mm/%dd",Date())
    CreateThread(@MyThread_Two(),@p2$)
    WaitSemaphore(semaphore)
    p2$="" ; Required here.

  EndIf

Until ev=#PB_Event_CloseWindow
User avatar
spikey
Enthusiast
Enthusiast
Posts: 778
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Re: Thread Parameters?

Post by spikey »

BarryG wrote: Sun Apr 21, 2024 4:10 am Sleep_(60000)
Just out of curiosity - why are you feeling the need to pause the threads for so long before exiting?
BarryG
Addict
Addict
Posts: 4219
Joined: Thu Apr 18, 2019 8:17 am

Re: Thread Parameters?

Post by BarryG »

The sleep is just for this example. In my real app, the threads do some processing that takes a while, so the sleep was used here to stop the thread exiting too early and to simulate the further processing.
User avatar
spikey
Enthusiast
Enthusiast
Posts: 778
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Re: Thread Parameters?

Post by spikey »

There's a pitfall that you should be aware of. A slightly different scenario, where the work takes place before the SignalSemaphone, could lock up the main event loop. I guess that's the sort of thing you're trying to avoid:

Code: Select all

Procedure MyThread_Two(param)
  date$=PeekS(param)
  Sleep_(60000)
  SignalSemaphore(semaphore)
  Debug "T2 -> "+date$ ; Should show the date.
EndProcedure
User avatar
Michael Vogel
Addict
Addict
Posts: 2819
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Thread Parameters?

Post by Michael Vogel »

@Berry - your solution doesn't work, just press the button multiple time and you'll see it...

There are many possibilities to deal with threads, I also would think about something like a 'main' thread otherwise you'd block all events in the main loop. Just a quick and dirty start which also would stop a running thread (AbortThread) if a new one gets initiated...

Code: Select all

; Enable thread-safety in Compiler Options.

Structure ParameterType
	p1.s
	p2.s
EndStructure

Global Main.ParameterType
Global Locker,AbortThread

#Timeout=1000

Macro Unlock(Lock)

	Lock=#Null

EndMacro
Macro LockAndGo(Lock)

	If Lock
		AbortThread=#True
		While Lock
			Delay(2)
		Wend
	EndIf
	Lock=#True

EndMacro
Macro ExitIf(condition)
	If condition
		Break
	EndIf
EndMacro

Procedure MyThread_One(param.s)

	Protected t

	Debug "T1 -> "+param

	While t<#Timeout
		ExitIf(AbortThread)
		Delay(1)
		t+1
	Wend

EndProcedure

Procedure MyThread_Two(param.s)

	Protected t

	Debug "T2 -> "+param

	While t<#Timeout
		ExitIf(AbortThread)
		Delay(1)
		t+1
	Wend

EndProcedure

Procedure MasterThread(*nil.ParameterType)

	Protected Local.ParameterType

	Debug "Start Thread..."
	LockAndGo(Locker)

	With Local
		\p1=*nil\p1
		\p2=*nil\p2

		Debug "Start 1"
		MyThread_One(\p1)

		If AbortThread=#Null
			Debug "Start 2"
			MyThread_Two(\p2)
		EndIf
	EndWith
	
	Unlock(Locker)
	AbortThread=#Null

	Debug "...Done"

EndProcedure


OpenWindow(0,400,200,170,80,"test",#PB_Window_SystemMenu)
ButtonGadget(0,20,20,130,30,"Click this repeatedly")
AddWindowTimer(0,0,500)
Repeat
	Select WaitWindowEvent()
	Case #PB_Event_Gadget

		With Main
			\p1=FormatDate("%hh:%ii:%ss",Date())
			\p2="..."

			CreateThread(@MasterThread(),@Main)
		EndWith

	Case #PB_Event_Timer
		SetWindowTitle(0,"t"+Chr(105+6*t&1)+"ck")
		t+1
		
	Case #PB_Event_CloseWindow
		AbortThread=#True
		LockAndGo(Locker)
		End
	EndSelect
ForEver
Last edited by Michael Vogel on Sun Apr 21, 2024 7:23 pm, edited 1 time in total.
User avatar
STARGÅTE
Addict
Addict
Posts: 2260
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: Thread Parameters?

Post by STARGÅTE »

BarryG wrote: Sun Apr 21, 2024 4:10 am Okay, this seems to be working for me in the way I need it. I can't see anything wrong with this approach? The goal is to show the time and date, from different threads, each time the button is clicked. Having one single global semaphore doesn't seem to be a problem? I'm not overlooking anything?
Yes, it is fine.
I can't see a problem suggested by Michael Vogel, of cause you have to enable Thread Safe.
And yes, the read out of the Parameter have to be the first step and then SignalSemaphore to unlock the main thread.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
User avatar
mk-soft
Always Here
Always Here
Posts: 6320
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Thread Parameters?

Post by mk-soft »

Semaphores are good for synchronising threads but not good for updating the GUI. I only use semaphores with GUI if the thread also has to wait for input from the GUI.

Use PostEvent if the GUI needs to be changed by the thread.
See examples 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
BarryG
Addict
Addict
Posts: 4219
Joined: Thu Apr 18, 2019 8:17 am

Re: Thread Parameters?

Post by BarryG »

Michael Vogel wrote: Sun Apr 21, 2024 12:21 pm@Berry - your solution doesn't work, just press the button multiple time and you'll see it
Seems to work fine here? Here's a sample debug output when I rapidly click the button. It shows each new thread alternating (the "T1" line followed by a "T2" line) with no misses. The idea is not to stop or abort a thread, as your example seems to do. Just to create those two threads as new threads each time the button is clicked, and let them time out and exit naturally.

Code: Select all

T1 -> 17:47:59
T2 -> 2024/04/22
T1 -> 17:47:59
T2 -> 2024/04/22
T1 -> 17:47:59
T2 -> 2024/04/22
T1 -> 17:47:59
T2 -> 2024/04/22
T1 -> 17:48:00
T2 -> 2024/04/22
T1 -> 17:48:00
T2 -> 2024/04/22
T1 -> 17:48:00
T2 -> 2024/04/22
T1 -> 17:48:00
T2 -> 2024/04/22
T1 -> 17:48:00
T2 -> 2024/04/22
[...]
AZJIO
Addict
Addict
Posts: 2225
Joined: Sun May 14, 2017 1:48 am

Re: Thread Parameters?

Post by AZJIO »

Code: Select all

Global param$
Global semaphore = CreateSemaphore()

Procedure MyThread_One(param)
	Debug "Cook: preparing food"
	Sleep_(3000)
	SignalSemaphore(semaphore)
	Debug "Cook: the food is ready, you can eat"
EndProcedure

Procedure MyThread_Two(param)
	Debug "Client: I eat food"
	Sleep_(3000)
	SignalSemaphore(semaphore)
	Debug "Client: ate food"
EndProcedure

OpenWindow(0, 400, 200, 170, 80, "test", #PB_Window_SystemMenu)
ButtonGadget(0, 10, 20, 150, 30, "Let's start the day")

Repeat

	ev = WaitWindowEvent()

	If ev = #PB_Event_Gadget And EventGadget() = 0
		DisableGadget(0, 1)

		p1 = 1
		For i = 1 To 3
			CreateThread(@MyThread_One(), @p1)
			WaitSemaphore(semaphore)
			
			CreateThread(@MyThread_Two(), @p1)
			WaitSemaphore(semaphore)
		Next

		DisableGadget(0, 0)

	EndIf

Until ev = #PB_Event_CloseWindow
User avatar
Michael Vogel
Addict
Addict
Posts: 2819
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Thread Parameters?

Post by Michael Vogel »

BarryG wrote: Mon Apr 22, 2024 8:53 am Seems to work fine here?...
Tried it multiple times here before I wrote the post, most of the time the code was stable, but at least two times I got an memory access error when closing the window :oops:
Today I tested your code again 30+ times with no error, not sure if the PB IDE (or myself) was in an unstable state some days before :wink:

Aborting an thread may not be useful while the app is running but I usually have such a feature for a clean exit of a program. I'd never use 'End' to shut down all unfinished threads nor 'KillThread' (because itself is unstable). Waiting until all threads have been finished can be annoying for the user.

Nothing new here, only a counter to see how many threads get killed when ending the program:

Code: Select all

; Enable thread-safety in Compiler Options.

Global ActiveThreads

Global param$
Global semaphore=CreateSemaphore()

Procedure MyThread_One(param)
	Protected Dim a.q(99999999)
	ActiveThreads+1
	Debug "init..."
	time$=PeekS(param)
	SignalSemaphore(semaphore)
	Debug "T1 -> "+time$ ; Should show the time.
	Sleep_(60000)
	ActiveThreads-1
	Debug "done..."
EndProcedure

Procedure MyThread_Two(param)
	ActiveThreads+1
	date$=PeekS(param)
	SignalSemaphore(semaphore)
	Debug "T2 -> "+date$ ; Should show the date.
	Sleep_(60000)
	ActiveThreads-1
EndProcedure

OpenWindow(0,400,200,170,80,"test",#PB_Window_SystemMenu)
ButtonGadget(0,20,20,130,30,"Click this repeatedly")
AddKeyboardShortcut(0,#PB_Shortcut_Return,0)
AddWindowTimer(0,0,500)

Repeat

	Select WaitWindowEvent()
	Case #PB_Event_Gadget,#PB_Event_Menu

		p1$=FormatDate("%hh:%ii:%ss",Date())
		CreateThread(@MyThread_One(),@p1$)
		WaitSemaphore(semaphore)
		p1$="" ; Required here.

		p2$=FormatDate("%yyyy/%mm/%dd",Date())
		CreateThread(@MyThread_Two(),@p2$)
		WaitSemaphore(semaphore)
		p2$="" ; Required here.

	Case #PB_Event_Timer
		; SetWindowTitle(0,"t"+Chr(105+6*t&1)+"ck, "+Str(GetProcessMemoryUsage(GetCurrentProcess_()))+" MByte")
		t+1

	Case #PB_Event_CloseWindow
		Debug "Active threads: "+ActiveThreads
		End; (see notes above)
	EndSelect

ForEver
[code]
Post Reply