An overview of the experimental thread module.

BlitzMax Forums/BlitzMax Tutorials/An overview of the experimental thread module.

TomToad(Posted 2008) [#1]
I thought I'd write a little tutorial about using threads with BlitzMAX. Threads are currently still experimental. The tutorial presented here is not complete and may even contain inaccuracies.

An overview of the experimental thread module.

This is not exactly a tutorial, but rather a quick overview of using threads in BlitzMAX. In order to use threads, you must use the svn version of BlitzMAX. I am not going to go into details on how to install svn, you can find info here.

http://www.blitzbasic.com/Community/posts.php?topic=74439 <- for svn info
http://www.blitzbasic.com/Community/posts.php?topic=80344 <- for getting threading capabilities up and running.

Now on to the tutorial

Part 1. Creating threads.
First you need a thread to run. This is done by creating a function that takes an object as its parameter and returns an object.
SuperStrict

Function Hello:Object(data:Object)
	Print "Hi!  I'm a thread!"
End Function

That's it! You now have a thread you can spawn any time. To spawn the thread, just make a call to CreateThread(). CreateThread() takes two parameters, a reference to the thread function and the object to pass to it. CreateThread() then returns a handle which is then used to close the thread later on.
Local Thread:Int = CreateThread(Hello,Null)

That's all. You now have a runnable program. If you try running the above 5 lines, you might find that sometimes it prints "Hi! I'm a thread" and sometimes not. That's because when the main program exits, all running threads are automatically closed. Since the program ends immediately after spawning the thread, it's possible that it will be closed before the Print can take place. So we'll add a delay in the example.
SuperStrict

Function Hello:Object(data:Object) 'The thread function
	Print "Hi!  I'm a thread!"
End Function

Local Thread:Int = CreateThread(Hello,Null) 'Create the thread

Delay(1000) 'Delay to allow the thread to complete

Part 2. Closing thread handles
Closing a thread handle means freeing it's handle back to the system so it can be used for other purposes. This is different from terminating a thread. Termination happens automatically when the thread ends or when the program exits.
There are two ways to close a thread handle. WaitThread() will wait until a thread finishes, then will close its handle. DetachThread() will close the handle immediately, allowing the calling thread to continue. The detached thread will continue to execute. This means you do not have to worry about polling the thread continuously to see if it has ended or not.

In our example above, we will replace the delay() with WaitThread(). Since WaitThread() also returns an object, we will make use of that as well.


Part 3. Mutex
Mutexes are basically gatekeepers to shared resources. If two threads need access to the same resource at the same time, then unpredictable results could happen. Anything from incorrect data to crashes could happen. Mutexes are not necessary all the time, if threads are only going to read data but not write to it, for example.
First I'm going to show you an example of threads sharing a resource without mutexes. The resource will be the console and the threads will try and print messages concurrently.

Wow, what a mess. Letters from one thread are printing between letters from the other. To prevent that, let's use a mutex.
To create the mutex, just use CreateMutex(). CreateMutex() returns a handle which can be locked and unlocked when needed. When a thread needs access to a resource, just call LockMutex(). If the mutex has already been locked by another thread, then the thread will wait until it has been unlocked before locking it itself. When the thread is through with the resource, it calls UnlockMutex() so that other threads can use it as well.

Now everything prints as expected. Or does it? You might actually see the Good Bye lines printed before the Hello lines in certain circumstances. It just depends on which thread gets to that LockMutex() first. So how would you make sure threads work in a certain order? For that, you can use Conditions.

Part Whatever: Conditions
Conditions are like traffic signals. You sit at a red light until it turns green. then you go.
To create a condition, you use CreateCond(). CreateCond() will return a handle. You will also need to create a mutex to use with the condition.
To wait for the condition to be signaled, you use WaitCond(). The parameters for WaitCond() are the condition handle and the mutex handle. To signal a condition, you use either SignalCond() or BroadcastCond(). Both take the condition handle as the only parameter. WaitCond(), SignalCond(), and BroadcastCond() all need to be wrapped with LockMutex() and UnlockMutex() commands, using the mutex created for the conditions.


The difference between SignalCond() and BroadcastCond() is that SignalCond() will only signal one thread to continue. If more than one thread is waiting for the signal, the one that gets the signal will be random. BroadcastCond() will signal all threads waiting for the signal.


ImaginaryHuman(Posted 2008) [#2]
Cool, this is nice and easy to follow, thanks for writing it and explaining it clearly!


plash(Posted 2008) [#3]
You should use code tags for the smaller/one liners instead of codeboxes, as they make reading a bit hazy.

Looks good! (reading now)


deps(Posted 2008) [#4]
Very interesting read, thanks a lot!


GfK(Posted 2008) [#5]
Explains this stuff very well. Thanks!