Managing multiple threads with `local variables`

BlitzMax Forums/BlitzMax Programming/Managing multiple threads with `local variables`

ImaginaryHuman(Posted 2008) [#1]
Let's assume I create a threaded BlitzMax app in which I set up x number of additional threads, besides the main one. This needs to be totally scalable so there could be 1 thread or 1000. This will be running on computers with multiple cores/cpu's.

Each thread will be executing exactly the same set of functions (and the list is quite long). It is not an option to duplicate any of the functions or to create a version of each function for each thread, because that's not scalable.

I want each individual thread to have kind of its own set of special `local variables`, or a single global variable which is an index of one dimension of a multidimensional array - just some way of isolating some data to be only used by that thread.

Each thread should access only the set of data `attached` to it, and not the data that other threads are using. Doing this with Processes is not an option (although processes do have local memory spaces). It's not just a matter of using mutexes, because mutexes are for isolating access to *shared* data, I need to actually get each thread its own unique set of data.

How do I make this work? Is there any way?


ImaginaryHuman(Posted 2008) [#2]
Upon some googling, I think what I'm trying to set up is `thread local storage`, but without using official API's. Unfortunately I don't yet see a way of doing this, unless I can use the handle from CreateThread() as some kind of lookup via a hash function or something?


Arowx(Posted 2008) [#3]
Err possibly a silly question but, won't any variables and data created by the thread be local to the thread, therefore if they launch lots of the same functions each threaded function will have it's own local data allocated?

Doesn't the CoffeeInc example do this?


TomToad(Posted 2008) [#4]
I'm not sure exactly what you want. Each thread has it's own memory space just by declaring variables as Local within the thread function.
There is AllocThreadData(), GetThreadData(), and SetThreadData() which deal with TLS, but so far, I haven't figured out what the advantage to them is over just declaring Local variables.


TaskMaster(Posted 2008) [#5]
I think he wants variables static to the function, so each time a specific thread calls the function, it remembers its variables. but, if a different thread calls the function, it gets the values of the last time it called the function...

The only way I can think of doing it is have a set of globals for each thread.


ImaginaryHuman(Posted 2008) [#6]
TomToad: Are those methods already available as part of the threading module, or are those o/s API calls? (I've heard that `real` thread-local-storage API's are relatively slow compared to a manually coded technique?)

I should have clarified about local variables. Yes locals will be temporary variables confined to the thread, but that's not what I'm asking. I need to use globals.

The problem is, the thread is going to be calling a number of functions and some of the functions are going to modify data that affects which functions the thread is going to call in future. So decisions made within the functions affect which functions get called from the higher level.

For efficiency, this requires that the data determining which functions get called be stored in Global variables (and because a function can't really modify a local variable outside the scope of the function - and passing return values is not efficient enough or flexible enough). The globals are therefore accessible to all threads, but each thread needs its *own set* of Globals. The threads won't share the globals - the data isn't global for enabling threads to share them, they're global to enable the functions to modify and interact with the thread's workload.

In other words, the thread modifies and allocates its own workload via the very functions it calls which are a part of its workload. It's a circular thing and I need globals to make it work, just not the same globals for each thread. You could call it a `self managing thread pool`.

I don't really need the variables to be static, the threads will be forming a thread pool and if the time comes for a thread to be deleted it's okay to lose its global data.

My system is working for one thread (ie not using threads) but I need it to be fully scalable to a large number of threads in a pool. I figured if I just simply put all my globals into another level of array dimensions, and somehow associate an index in the arrays with each thread, then I can get each thread to modify a unique set of global data. But I don't see how I can do that because somehow the thread needs to be associated with an index number which has to be stored somewhere in such a way that the thread's workload functions can know what array elements to modify. So how can I do that?


ziggy(Posted 2008) [#7]
Make a TThread class and set fields


ImaginaryHuman(Posted 2008) [#8]
Okay but how does each thread get associated with an instance of TThread?

Whether I use a type or a global array isn't so important, but I don't see how to get a specific thread to access a specific instance/index that other threads aren't accessing. Do you pass the TThread as an object into CreateThread? And if so, how do the thread functions get global access to that?


TomToad(Posted 2008) [#9]
The type is passed to the thread through the CreateThread() function. Just be sure to cast it when entering the thread, like this
Function Thread:Object(data:Object)
    Local ThreadData:TThread = TThread(data)
...etc
End Function

Local ThreadData:TThread = New TThread
Local ThreadHandle:Int = CreateThread(ThreadData)

To use it in a function, just pass the type to the function normally. Don't worry about speed. You are only passing the reference to the type which is really just an Int pointer. It is no slower than creating an array then passing an index to the function. Actually types may be more efficient since you will probably need to create some sort of referencing system for the array, or else the array can get quite huge.


ImaginaryHuman(Posted 2008) [#10]
Thanks TomToad.

I've been giving this a lot of thought over the past few days and I think the standard/your way is probably what I will have to do. Not necessarily because it's how I want it to work but because there really isn't any other way to do it. I ideally want to have global variables which you call with the same variable name but which actually pull up different contents depending on what thread you're in - and the only way to do that is to add a layer of abstraction and some kind of index or custom types.

I will also look at using "AllocThreadData(), GetThreadData(), and SetThreadData()" to store the custom thread type instance as real thread-local-storage, because then I don't have to pass it to *every* function, only the ones that need it which can go get it on a per-function basis. Real thread-local storage is basically storage that you request from the operating system to be associated with the thread that is running the code accessing it, basically like using Local variables but without any context restrictions other than that it's part of the thread - and you don't seem to need to know any `ID` of the thread you're running in order to assign the storage or use it.

I also think I'm going to store what used to by my globals as fields in the custom thread type. I did some speed tests of using single dimensional arrays vs multi dimensional vs custom types accessing fields and they are quite similar with types being a bit faster most of the time.