CreateThread

BlitzMax Forums/BlitzMax Beginners Area/CreateThread

Hardcoal(Posted 2016) [#1]
can anyone exaplain what threads are in blitzmax
how to use CreateThread, whats the advantages
or maybe direct to an example or a guide.
tnx


col(Posted 2016) [#2]
:-)

In the simplest terms... a thread is a function with the code that you to want to run inside that function, of course this function call other functions too in the normal way that you would in your main code. Each thread also has its own stack and own set of cpu registers. However all threads in the current process have unrestricted access to the global variables, which is where the fun starts :p

I think threads are initially confusing for Blitzers because the 'main' function is abstracted behind the scenes and the code that you initially type into the editor is called from the 'main' function. In other languages you have to specify the 'main' function as your programs entry point and its the main thread that runs the main function. Your 'regular' blitzmax code is running in the main thread. Knowing this you can then understand why you need a function that will run in a thread when you create a new thread. Also because of cpus having more than one core, where each core is actually another complete self contained cpu, other threads are able to run on the other cores allowing you to run multiple threads at the same time without any slow down what so ever. The OS does also provide a mechanism to run multiple threads on single core cpus, and also run many threads on each of the multiple cores, but each threads that runs on a each core is switched in and out during execution giving the illusion that there's more than threads running at the same time - no speed gains can be had from multithreading a single core cpu only from multicore processors. The best gains are if you can have 1 extra thread per core. The OS is clever enough to figure out how to distribute your threads across the cores evenly.

Of course there are caveats to having multiple threads running at the same time which all have access to the Globally defined variables, most notably that of synchronisation. What this essentially means is that its possible to have one thread read the value of a variable as another thread is writing to it - which is naturally cause for concern! Depending on what you're doing then sometimes it doesn't matter but 99% of the time it will matter and will need to be handled one way or another.
The good thing is as we are late to the party ( threading and synchronisation have been known about since the dawn of cpus in the '60s ) then these things have been thought of with building blocks in place to handle the synchronisation part of things. Look up Mutex,Semaphore,Condition Variables and atomic operations - although sadly their explanations are made even more confusing for some due to trying to explain what they are using situations that don't even relate to computers.

Heres an example of 2 threads running at the same time, nothing special...
Notice that I'm using WriteStdout to write to the console, this is because WriteStdout is thread safe - you can call it from any thread without worrying about any sync issues. Change WriteStdout to Print and you will see sync issues at work first hand...
' This code is running in the main thread
CreateThread( SecondThread,Null)

For Local i = 0 Until 100
	' This delay is only to allow the 2nd thread time to be created and put into a running state
	Delay 2
	WriteStdout "Writestdout is thread safe~n"
Next

' This will be run in our new 2nd thread
' The new thread can take any object and also return any object
Function SecondThread:Object(Data:Object)
	For Local i = 0 Until 50
		Delay 1
		WriteStdout "Called from out second thread~n"
	Next
EndFunction



col(Posted 2016) [#3]
When it comes to syncing your threads you may ask why do you need to bother.
Here's a simple run down of why sync is needed...

You have 2 threads running asynchronously ( at the same time ). You have a global integer variable that has say the value of 2. Threads can call the same functions safely at the same time as they have their own cpu registers and their own stacks. So what happens when in that function they want to change the value of the global variable. Lets say the function increments the global variable by 1. What this mean is thread 1 will increment the value by 1 and thread 2 will also increment the value by 1. So after the two threads have simultaneously executed the little part of a function to increment to the variable then the variable value should be 4 yes? Lets see what happens when both threads execute the increment at the same time without any sync...

The function could look something like this and remember that 2 threads can run this function at the same time
Function IncrementGlobal()
	var :+ 1
EndFunction


Thread 1 - READ - Reads variable value from memory and puts it in a cpu register - ( Value of 2 )
Thread 1 - MODIFY - Increments the value in the cpu register ( Value of 3 )
Thread 2 - READ - Read the variable from memory and puts it in to its own cpu register ( Value of 2 )
Thread 1 - WRITE - Writes out the value of 3 to the memory for the variable.
Thread 2 - MODIFY - Increments the value in its register ( Value of 3 )
Thread 2 - WRITE - Writes its cpu register value back to memory to the variable address. ( Value of 3 )

Variable value is now 3!! Even though 2 threads have incremented the value that started out as 2.
This is what is a called 'a race condition' among multithread connoisseurs.

So what is needed is some kind of synchronisation. The easiest, but not only, sync mechanism to use is a mutex to allow mutual exclusion for 1 thread to do the increment at a time. Locking a mutex is also thread safe and be called from any thread without fear of 2 threads locking it at the same time. Lets see happens when we use a mutex in the function used to increment the global variable...
Function IncrementGlobal()
	LockMutex(mutex)
	var :+ 1
	UnlockMutex(mutex)
EndFunction



Thread 1 - Locks the mutex
Thread 1 - READ - Read the variable into cpu register - value of 2
Thread 1 - MODIFY - Increment the cpu register value to 3
Thread 2 - Lock the mutex - cannot lock as its already locked by thread 1 so thread 2 now has to wait until the mutex is unlocked.
Thread 1 - WRITE - Write the value of 3 to the variable address
Thread 1 - Unlocks the mutex
Thread 2 - READ - As the mutex is now unlock the thread can continue. Thread 2 now locks the mutex and reads the variable value of 3 into cpu register
Thread 2 - MODIFY - Increment cpu register value to 4
Thread 2 - WRITE - Write the value to variable address
Thread 2 - Unlock the mutex

So now the variable value is 4, as we expected!

When you get a little more experienced you may have noticed that its perfectly safe to READ the value from multiple threads if all you want to do is READ it. Under some situations that may be all you want to do so in that case there's no need to sync at all! but let's leave that for another time :p


Hardcoal(Posted 2016) [#4]
Dave your explanations are gold.
tnx for the explanation and the demo..

although i knew what threads are in general
though it was based on my assumptions only ,
but you verified most of what ive thought threads are..
atm i will not mess with it too much..
but i will get deeper into it at some point.