multithreaded/threadlocaldata - Maple Help
For the best experience, we recommend viewing online help using Google Chrome or Microsoft Edge.

# Online Help

###### All Products    Maple    MapleSim

Home : Support : Online Help : multithreaded/threadlocaldata

Thread Local Data

maintaining a thread local state

 Calling Sequence module() local l1::thread_local, l2::thread_local( type ); ... end module

Parameters

 l1 - a local name l2 - a local name type - a type

Description

 • 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}$ (1)
 > shared:-routine( 10^6, 2*10^6 );
 ${1500001500000}$ (2)
 • 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 ] );
 ${782728998607}{,}{1736054835546}$ (3)
 • 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;
 ${\mathrm{thrlocal}}{:=}{\mathbf{module}}\left({}\right)\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathbf{local}}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathrm{state}}{::}{\mathrm{thread_local}}{;}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathbf{export}}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathrm{routine}}{;}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathbf{end module}}$ (4)
 > thrlocal:-routine( 1, 10^6 );
 ${500000500000}$ (5)
 > thrlocal:-routine( 10^6, 2*10^6 );
 ${1500001500000}$ (6)
 > Threads:-Task:-Start( passed, Task=[ thrlocal:-routine, 1, 10^6 ],                                 Task=[ thrlocal:-routine, 10^6, 2*10^6 ] );
 ${500000500000}{,}{1500001500000}$ (7)

Default Values

 • 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}$ (8)
 > 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}$ (9)
 > 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}$ (10)

thread_local and Tasks

 • 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