Thread Local Data
maintaining a thread local state
Calling Sequence
Parameters
Description
Default Values
thread_local and Tasks
module() local l1::thread_local, l2::thread_local( type ); ... end module
l1
-
a local name
l2
type
a type
Many algorithms maintain data during their computation. In Maple, this data is often stored as module locals. By default module locals are shared variables, meaning that their value can be accessed by any thread. This can make these algorithms difficult to make thread-safe. To solve this, Maple allows module locals to be declared thread local.
A thread local name stores a different value for each Maple thread that accesses the name. This means that each thread can access the value stored by that name without worrying about interactions between threads. In particular, there is no need to use a Mutex to synchronize access to the name.
To declare a module local as thread local, the thread_local type modifier is used:
m := module() local shrdLocal, thrLocal::thread_local; ... end:
In this example, shrdLocal is a shared module local. The value stored by shrdLocal can be accessed and modified by any thread. The variable thrLocal is thread local, the value it stores is specific to a thread.
The thread_local modifier can also be combined with a type declaration similar to a procedure parameter modifier. In the following example, thrlocal is declared to be thread local and of type integer.
m := module() local modLocal::integer, thrLocal::thread_local(integer); ... end:
In the following example, the shared module's state variable is not thread local.
shared := module() local state; export routine; routine := proc(lo,hi) local i; state := 0; for i from lo to hi do state := state + i; end; state; end; end:
When run sequentially, this works as expected.
shared:-routine( 1, 10^6 );
500000500000
shared:-routine( 10^6, 2*10^6 );
1500001500000
However if these example are run in parallel the threads interfere with each other by accessing the shared state variable.
Threads:-Task:-Start( passed, Task=[ shared:-routine, 1, 10^6 ], Task=[ shared:-routine, 10^6, 2*10^6 ] );
808815548649,1488150587496
By using the thread_local type modifier we can make this example work properly in parallel.
thrlocal := module() local state::thread_local; export routine; routine := proc(lo,hi) local i; state := 0; for i from lo to hi do state := state + i; end; state; end; end;
thrlocal:=modulelocalstate::thread_local;exportroutine;end module
thrlocal:-routine( 1, 10^6 );
thrlocal:-routine( 10^6, 2*10^6 );
Threads:-Task:-Start( passed, Task=[ thrlocal:-routine, 1, 10^6 ], Task=[ thrlocal:-routine, 10^6, 2*10^6 ] );
500000500000,1500001500000
A thread local variable can be assigned a default value, that is, the value that will be seen by any thread that has not yet assigned to the variable. The default value is the final value assigned to the variable during the evaluation of the module body.
defaults1 := module() local state::thread_local := 1; export get; get := proc() state; end; end:
Threads:-Task:-Start( passed, Task=[ defaults1:-get ], Task=[ defaults1:-get ] );
1,1
defaults2 := module() local state::thread_local := 1; export get; state := 2; get := proc() state; end; end:
Threads:-Task:-Start( passed, Task=[ defaults2:-get ], Task=[ defaults2:-get ] );
2,2
defaults3 := module() local state::thread_local := 1; export get; state := 2; get := proc() state; end; state := 3; end:
Threads:-Task:-Start( passed, Task=[ defaults3:-get ], Task=[ defaults3:-get ] );
3,3
Maple's Task Programming Model is a high level parallel programming model. The Task Model does not specify which thread a task will be executed on. Thus one must be careful when combining algorithms written with the Task Model and thread_local names. In general, when using thread_local data with the Task model, the algorithm should not share data between tasks using thread_local modules locals.
One useful exception is an algorithm that uses a temporary memory buffer. Each thread can create a single buffer that is used for the computation in the current task. Once the task is complete, the buffer can be left for the next task running on that thread to re-use. In this case, only the buffer is passed between tasks, not data used for the computation.
See Also
index/threadsafe
module
module[local]
multithreaded
Task Programming Model
Download Help Document