[Template] Synchronous Threaded Worker

Share your advanced PureBasic knowledge/code with the community.
User avatar
Azias
New User
New User
Posts: 7
Joined: Wed Oct 04, 2017 5:59 pm

[Template] Synchronous Threaded Worker

Post by Azias »

Hello,

I recently needed to play with threaded workers and after I finished cleaning up some old code I thought it could be made into a template, so here it is.

Just keep in mind that this may not be the most optimal way to go about doing threaded workers, but it helps you navigate around some restrictions of the language and it's easy enough to customize.

Code: Select all

;{- Code Header
; ==- Basic Info -================================
;         Name: SynchronousThreadedWorkerTemplate.pb
;      Version: 0.0.1
;      Authors: Herwin Bozet
;  Create date: Friday, ‎22 ‎May ‎2020, ‏‎13:35:16
; 
;  Description: N/A
; 
; ==- Compatibility -=============================
;  Compiler version: PureBasic 5.70 (x64) (Other versions untested)
;  Operating system: Windows 10 (Other platforms untested)
; 
; ==- License -===================================
;  License: Unlicense
;  
;}


;- Compiler directives & imports
;{

EnableExplicit

CompilerIf #PB_Compiler_Thread = 0
	CompilerError "Required: Thread-safe compiler flag."
CompilerEndIf

;}


;- Constants & Structures
;{

#WORKER_POOL_SIZE_DEFAULT = 64
#WORKER_POST_BIRTH_DELAY = 50 ; ms

; A struct is used to pass more than 1 argument to threads
Structure CustomThreadParameterStruct
	CustomMutex.i ; Not sure about the type
	
	; [Custom variables here]
EndStructure

;}


;- Procedures
;{

Procedure CustomWorkerThread(*Parameters.CustomThreadParameterStruct)
	
	;[Pre-lock code]
	
	LockMutex(*Parameters\CustomMutex)
	
	;[Locked code]
	
	UnlockMutex(*Parameters\CustomMutex)
	
	;[Post-lock code]
	
EndProcedure

;}


;- Code
;{

; These 2 lists could be arrays, but they are annoying to use in PB.
; And the small performance tradeoff is worth it IMHO.
NewList WorkerPoolThreadIDs.i()
NewList WorkerPoolThreadParams.i()

Define CustomMutex = CreateMutex()

Define iWorkerPool.i
Define *TemporaryParamsPtr.CustomThreadParameterStruct

Define WorkerPoolSize = #WORKER_POOL_SIZE_DEFAULT


; Initializing the worker lists...
For iWorkerPool = 0 To WorkerPoolSize - 1
	InsertElement(WorkerPoolThreadIDs())
	WorkerPoolThreadIDs() = #Null
	
	InsertElement(WorkerPoolThreadParams())
	WorkerPoolThreadParams() = #Null
Next


; Spawning the threads...
While #False ; ### Change this to something appropriate for your use case ###
	ForEach WorkerPoolThreadIDs()
		If Not IsThread(WorkerPoolThreadIDs())
			
			; Cleaning up some garbage (leftovers from previous threads)
			SelectElement(WorkerPoolThreadParams(), ListIndex(WorkerPoolThreadIDs()))
			If WorkerPoolThreadParams()
				FreeMemory(WorkerPoolThreadParams())
			EndIf
			
			WorkerPoolThreadParams() = AllocateMemory(SizeOf(CustomThreadParameterStruct))
			*TemporaryParamsPtr = WorkerPoolThreadParams()
			*TemporaryParamsPtr\CustomMutex = CustomMutex
			
			WorkerPoolThreadIDs() = CreateThread(@CustomWorkerThread(), *TemporaryParamsPtr)
			
			; Staggers the launch of future threads (not required !)
			Delay(#WORKER_POST_BIRTH_DELAY)
		EndIf
	Next
Wend


; Waiting for the last threads to finish...
ForEach WorkerPoolThreadIDs()
	If IsThread(WorkerPoolThreadIDs())
		WaitThread(WorkerPoolThreadIDs())
	EndIf
Next


; Cleaning up...
ForEach WorkerPoolThreadParams()
	If WorkerPoolThreadParams()
		FreeMemory(WorkerPoolThreadParams())
	EndIf
Next

Debug "Done !"

;}
Image