'MultiThreading. Re-using the same function 'IDC & Dr. X - Dec 2007 ' with code help from several demos ' This is an example of how to create a thread and pass arguments to it. BCX_THREAD does not allow passing args. ' It also shows how the same thread can be called multiple times simultaneously to act on different data and prevent ' the running threads from destroying the data of other threads. It uses "Critical Sections" to accomplish this. ' For this demo we'll just write the data to files. If this were NOT done correctly (by not using CriticalSections), ' then the 3 instances of the thread would all try to write to the same file instead of the 3 separate files indicated. ' They would also write the wrong data as they would be constantly over writing variables that were in use. 'This TYPE needs to hold all the variable data for each thread ... TYPE threadArgs 'use it to hold any args that need to be passed to the function or sub. Dim A$ ' filename DIM hThread as HANDLE ' handle to thread DIM id ' thread id Dim Done ' completed flag Dim Counts ' how many lines to produce Dim Counter ' how many lines so far Dim theText as string * 128000 ' somewhere to hold the string END TYPE GLOBAL wrifile AS CRITICAL_SECTION 'define a critical section. GLOBAL Thread_counter 'keep count of how many threads are running DIM myThreadsArgs[10] as threadArgs 'the args passed to the function 'Put some data in the types for use in the functions myThreadsArgs[1].A$ = "fileone.txt" : KILL myThreadsArgs[1].A$ myThreadsArgs[2].A$ = "filetwo.txt" : KILL myThreadsArgs[2].A$ myThreadsArgs[3].A$ = "filethree.txt" : KILL myThreadsArgs[3].A$ myThreadsArgs[1].Counts = 1000 myThreadsArgs[2].Counts = 200 myThreadsArgs[3].Counts = 600 InitializeCriticalSection(&wrifile) 'initialization MUST happen BEFORE starting any of the threads 'add 3 threads, updating how many threads are running For integer a = 1 to 3 'there will be 3 duplicate threads running. Thread_counter = AddThread(Thread_counter) 'each thread will be named for this variable 'counter' obtained from Function AddThread() ' Next a 'check the threads are running While Thread_counter <> 0 'while any of the threads are still running For integer y = 1 to 3 If myThreadsArgs[y].Done = 1 then 'check for the "done" completion flag Thread_counter-- 'if one is found, then lower the thread counter by 1 myThreadsArgs[y].Done = 0 'as the threads finish then clean up print "DONE! " & str$(y) 'and report it as complete End If Next DoEvents() WEND 'we're ending so end the CriticalSection DeleteCriticalSection(&wrifile) 'once all the threads are ended, we no longer need the critical section 'pause END Function AddThread(counter) counter++ 'creating a uniquely numbered handle for each thread we create. myThreadsArgs[counter].id = counter ' make the id same as counter myThreadsArgs[counter].hThread = CreateThread(NULL, _ 0, _ TheFunc, _ 'the name of the function &myThreadsArgs[counter], _ 'pass args to the function via this element (includes the counter number) 0, _ NULL) Function = counter End Function 'Our Thread, receive the ptr to the type Function TheFunc (BYREF MyThreadVar AS threadArgs) 'BCX is giving errors writting to files directly from the type so I need some locals to copy to local x$ * 128000 local SomethingToDo local rn Dim strTmp$ x$ = (MyThreadVar).A$ print "START! " & x$ RANDOMIZE(TIMER) 'while the file is open lets not other threads mess with us EnterCriticalSection(&wrifile) 'anything that can be stomped on by other threads is critical * open (MyThreadVar).A$ for append as fp1 ' open the file x$ = (MyThreadVar).A$ ' grab the arg passed to the fucntion fprint fp1, "this is the arg$: " & x$ & " " & str$(TIMER) ' write it to the file close fp1 ' close the file LeaveCriticalSection(&wrifile) 'end of critical section (*treat these like tags. enclose critical stuff) 'do some work for (MyThreadVar).Counter = 1 to (MyThreadVar).Counts 'do some stuff with the data. anything really. rn = fix(RND*10) sleep(rn) 'using a random sleep timer just to help the threads finish at different times.... x$ = (MyThreadVar).A$ 'giving this foney work an element of randomness. Like the function was really doing... SomethingToDo = (MyThreadVar).Counter ' work in the real world. Change rn to be ...RND*100 or more if you can bare it. strTmp$ = str$(SomethingToDo) & " : " & " this should be in file: " & x$ & " Random sleep timer = " & str$(rn) & " Number of threads = " & str$(Thread_counter) & crlf$ CONCAT ((MyThreadVar).theText$, strTmp$ ) DoEvents() next 'while the file is open lets not allow other threads mess with us EnterCriticalSection(&wrifile) 'anything that can be stomped on by other threads is critical * 'save the data to file open (MyThreadVar).A$ for append as fp1 'the reason we open the file in a CriticalSecions is because we are opening x$ = (MyThreadVar).theText$ 'more than one file and it would be easy to store the data into the wrong file. fprint fp1, x$ close fp1 LeaveCriticalSection(&wrifile) 'end of critical section (*treat these like tags. enclose critical stuff) (MyThreadVar).Done = 1 'we are done with the thread so set the "done" flag so our main program knows it. x$ = (MyThreadVar).A$ print "DONE! " & x$ 'clean up ExitThread Function = 1 End Function