Funny you should say that...

Here is a generic parallel multi tasking script, which can easily be changed for most (any?)application:


Break ON
; vim>600:filetype=kix sw=3 ts=3
; vim>600:fdm=marker fdc=4

; Generic multi task launcher
$gNull=SetOption("Explicit","ON")
$gNull=SetOption("ASCII","ON")
$gNull=SetOption("WrapAtEol","ON")

Global $gControlFile
Dim $iMaxThreads,$sKixCommand

;********************************************************************************************
;************************BEGIN CUSTOMISED VARIABLE DEFINITION********************************
; Change the next three variable definitions to fit local requirements *
$gControlFile=@SCRIPTDIR+"\MultiTask.ini" ; Main control file. *
$iMaxThreads=10 ; Maximum child processes to start *
$sKixCommand="kix32.exe" ; KiXtart command used to start child processes *
;*************************END CUSTOMISED VARIABLE DEFINITION*********************************
;********************************************************************************************

; Decide if this is a child or parent task
If IsDeclared($gChildName) ; Child task.
$gNull=WriteProfileString($gControlFile,"STATUS",$gChildName,udfChildTask($gChildName,$gChildParameter))
Else ; Parent task
Dim $sChildParameter ; New thread parameter
Dim $sChildName ; Child name
Dim $iChildCount ; How many child processes are active

"Parent starting, maximum threads="+$iMaxThreads+@CRLF

; Set up control file
If RedirectOutput($gControlFile,1)
"FATAL: Cannot open control file for writing!"+@CRLF
" Error: "+@ERROR+", "+@SERROR+@CRLF
Exit 1
EndIf
"[STATUS]"+@CRLF
"[ACKNOWLEDGED]"+@CRLF
$gNull=RedirectOutput("")

If udfInitialiseParent()
"ERROR: Cannot initialise parent process."+@CRLF
Exit 1
Else
"Parent initialisation complete. Let's go ta woik..."+@CRLF

; Get each child process
$sChildParameter=udfEnumChildParameter()
While $sChildParameter
$sChildName="Child_"+Right("000000"+(Val(SubStr($sChildName,7))+1),6)
; Wait until a slot is available
While $iChildCount=$iMaxThreads $iChildCount=$iChildCount-udfCheckStatus() Loop
; Launch child
$iChildCount=$iChildCount+1
Run $sKixCommand+' '+@SCRIPTDIR+'\'+@SCRIPTNAME+' $$gChildName='+$sChildName+' $$gChildParameter="'+$sChildParameter+'"'
$sChildParameter=udfEnumChildParameter()
Loop

; Wait for all tasks to complete
"All child tasks started - waiting for "+$iChildCount+" to complete"+@CRLF
While $iChildCount $iChildCount=$iChildCount-udfCheckStatus() Loop
"Run completed."+@CRLF
EndIf

Exit 0

; This routine will check the status of running jobs and return the number that have completed.
Function udfCheckStatus() ; {{{
Dim $sChildName,$sStatus
$udfCheckStatus=0
For Each $sChildName In Split(ReadProfileString($gControlFile,"STATUS",""),Chr(10))
If $sChildName
$udfCheckStatus=$udfCheckStatus+1
$sStatus=ReadProfileString($gControlFile,"STATUS",$sChildName)
$sChildName+" reports completion status: "+$sStatus+@CRLF
$gNull=WriteProfileString($gControlFile,"STATUS",$sChildName,"")
$gNull=WriteProfileString($gControlFile,"ACKNOWLEDGED",$sChildName,$sStatus)
EndIf
Next
; Sleep a little to give up CPU between checks *IF* no jobs have completed.
If Not $udfCheckStatus Sleep 0.2 EndIf
EndFunction ; }}}

;----------------------------------------------------------------------------
;---------------------BEGIN CUSTOMISATION SECTION----------------------------
; Change the UDFS below this point to customise the multithreaded process.
; The demo code lines are all labelled "*DEMO*", change or remove as needed.

; Parent initialisation.
; Use this to set up structures and variables for the udfEnumChildParameter()
; Don't forget to make variables global if they are going to be used in the
; subsequent functions.
;
; The *DEMO* initialisation code will open a text input file.
;
Function udfInitialiseParent() ; {{{
$udfInitialiseParent=0 ; Default to "OK" state
Global $gfhInput ; *DEMO*
$gfhInput=FreeFileHandle() ; *DEMO*
If Not $gfhInput ; *DEMO*
"Fatal: Cannot allocate file handle"+@CRLF ; *DEMO*
$udfInitialiseParent=1 ; *DEMO*
Exit 1 ; *DEMO*
EndIf ; *DEMO*
If Open($gfhInput,"input.txt") ; *DEMO*
"Fatal: Cannot open input file"+@CRLF ; *DEMO*
$udfInitialiseParent=1 ; *DEMO*
Exit 1 ; *DEMO*
EndIf ; *DEMO*
Exit 0
EndFunction ; }}}

; Enumerate child parameter.
; Each time this function is called it should return a value which will be
; passed to the new thread as a parameter. It should return a null value when
; all thread parameters have been exhausted.
;
; The *DEMO* code will return a line from the input file opened in the parent
; initialisation each time it is called
;
Function udfEnumChildParameter() ; {{{
$udfEnumChildParameter="" ; Default to "List exhausted" state

$udfEnumChildParameter=ReadLine($gfhInput) ; *DEMO*
If @ERROR $udfEnumChildParameter="" EndIf ; *DEMO*
Exit 0
EndFunction ; }}}

; Child task.
; When a new thread is launched this function is called with the child parameter
; The function should return a string representing the status to be returned to
; the parent.
;
; The *DEMO* code just sleeps a random number of seconds, then returns the
; paramater in the status
Function udfChildTask($sName,$sParameter) ; {{{
$udfChildTask="0 OK" ; Default success message

Srnd(@MSECS*1000+Val(Right($sName,6))) ; *DEMO*
Sleep 3+Rnd(15) ; *DEMO*
$udfChildTask="0 '"+$sParameter+"'" ; *DEMO*
Exit 0
EndFunction ; }}}