ConditionVariable Overview - Maple Help

 ConditionVariable

 Calling Sequence Threads[ConditionVariable][command]( arguments ); command( arguments );

Description

 • The Threads[ConditionVariable] package provides user-level commands for condition variables.  Condition variables are used to synchronize the execution of multiple threads.  Condition variables can be seen as a better implementation of a polling loop.  They allow a thread to wait until a particular event occurs and then begin executing again.
 • Using condition variables, threads can pause their execution, waiting for another thread to tell them to continue.  The thread that signals the paused threads has the option of either starting one thread or all the threads waiting on the condition variable.
 • If multiple threads are waiting on a condition variable and only one is started, there is no guarantee as to which thread is started.
 • Using a condition variable also requires a mutex. You should become familiar with the use of mutexes before using condition variables.
 • A basic situation where a condition variable is used is when multiple threads are working and the threads need to exchange data at some point.  As the threads reach that point they place their data into a shared location and then wait on the condition variable.  When the final thread reaches the synchronization point, it can signal the other threads to wake up and all the threads can continue and access the shared data.
 • Another example of the use of a condition variable is the producer/consumer pattern.  In this pattern there is one thread, the producer, that is producing jobs (something that requires processing) and adding them to a list.  Other threads, the consumers, remove a job from the list and perform the required processing.
 • If a consumer thread is able to process a job, but none are available, then the thread will pause its execution by waiting on a condition variable. When the producer generates more jobs, it can signal the threads waiting on the condition variable to start processing the jobs.

 • The Create function is used to create a new condition variable.
 • The Destroy function is used to free the resources associated with a condition variable.
 • The Wait function is used by a thread to pause itself until it is signaled by another thread.
 • The Signal function is used by a thread to start one thread that is waiting on the condition variable.
 • The Broadcast function is used by a thread to start all the threads that are waiting on the condition variable.

Examples

This is an example of a Producer/Consumer pattern.

The Producer creates jobs (here represented by integers in the global table tab).  The Consumers remove a job, process it, then insert the result back into the table.

We have two condition variables and one mutex.  One condition variable (cp) is used for the Producer thread.  The Producer thread will pause on the producer condition variable if enough jobs are available for processing. The other condition variable (cc) is used for the Consumer threads.  When no jobs are available, the Consumer threads will pause on that condition variable until the Producer creates more jobs.

The mutex is used to protect access to the global variable tab.

 > Producer := proc( m, cp, cc, max, mindiff )     global tab, e;     local i,j,n;     Threads[Mutex][Lock]( m );     j := 0;     tab[ "maxjob" ] := mindiff;     tab[ "curjob" ] := 1;     for j from 1 to mindiff     do         tab[ j ] := 2*j;     end do;     Threads[ConditionVariable][Signal]( cp );     n := false;     while ( e )     do         j := tab[ "maxjob" ];         if ( j - tab[ "curjob" ] > mindiff/2 ) then             n := true;             Threads[ConditionVariable][Wait]( cp, m );         end if;         for i from j to tab[ "curjob" ] + mindiff         do             tab[ i ] := 2*i;         end do;         tab[ "maxjob" ] := tab[ "curjob" ] + mindiff;         if ( n ) then             Threads[ConditionVariable][Broadcast]( cc );             n := false;         end if;     end do;     Threads[Mutex][Unlock]( m ); end proc: Consumer := proc( m, cp, cc, max )     global tab, e;     local n, i, j, num;     num := 0;     Threads[Mutex][Lock]( m );     while ( num < max )     do         while ( tab[ "curjob" ] = tab[ "maxjob" ] )         do             Threads[ConditionVariable][Signal]( cp );             Threads[ConditionVariable][Wait]( cc, m );         end do;         n := tab[ "curjob" ];         j := tab[ n ];         tab[ "curjob" ] := n + 1;         Threads[Mutex][Unlock]( m );         j := add( i, i=1..j );         num := num+1;         Threads[Mutex][Lock]( m );         tab[ n ] := j;     end do;     Threads[Mutex][Unlock]( m ); end proc:
 > $\mathrm{tab}≔\mathrm{table}\left(\right):$
 > $m≔\mathrm{Threads}\left[\mathrm{Mutex}\right]\left[\mathrm{Create}\right]\left(\right)$
 ${m}{≔}{2}$ (1)
 > $\mathrm{cc}≔\mathrm{Threads}\left[\mathrm{ConditionVariable}\right]\left[\mathrm{Create}\right]\left(\right)$
 ${\mathrm{cc}}{≔}{1}$ (2)
 > $\mathrm{cp}≔\mathrm{Threads}\left[\mathrm{ConditionVariable}\right]\left[\mathrm{Create}\right]\left(\right)$
 ${\mathrm{cp}}{≔}{2}$ (3)
 > $e≔\mathrm{true}:$

Start the Producer thread. We wait on cp until the Producer thread has started.

 > $\mathrm{Threads}\left[\mathrm{Mutex}\right]\left[\mathrm{Lock}\right]\left(m\right)$
 > $\mathrm{id1}≔\mathrm{Threads}\left[\mathrm{Create}\right]\left(\mathrm{Producer}\left(m,\mathrm{cp},\mathrm{cc},31,10\right)\right)$
 ${\mathrm{id1}}{≔}{1}$ (4)
 > $\mathrm{Threads}\left[\mathrm{ConditionVariable}\right]\left[\mathrm{Wait}\right]\left(\mathrm{cp},m\right)$
 > $\mathrm{Threads}\left[\mathrm{Mutex}\right]\left[\mathrm{Unlock}\right]\left(m\right)$

Start the Consumer threads.  They will each consume 100 jobs and there are 5 threads so we should process 500 jobs.

 > $\mathrm{id2}≔\left[\mathrm{seq}\left(\mathrm{Threads}\left[\mathrm{Create}\right]\left(\mathrm{Consumer}\left(m,\mathrm{cp},\mathrm{cc},100\right)\right),i=1..5\right)\right]$
 ${\mathrm{id2}}{≔}\left[{2}{,}{3}{,}{4}{,}{5}{,}{6}\right]$ (5)

Wait for the Consumer threads to finish.

 > $\mathrm{Threads}\left[\mathrm{Wait}\right]\left(\mathrm{op}\left(\mathrm{id2}\right)\right)$
 > $\mathrm{Threads}\left[\mathrm{Mutex}\right]\left[\mathrm{Lock}\right]\left(m\right)$

 > $e≔\mathrm{false}:$
 > $\mathrm{Threads}\left[\mathrm{ConditionVariable}\right]\left[\mathrm{Signal}\right]\left(\mathrm{cp}\right)$
 > $\mathrm{Threads}\left[\mathrm{Mutex}\right]\left[\mathrm{Unlock}\right]\left(m\right)$
 > $\mathrm{Threads}\left[\mathrm{Wait}\right]\left(\mathrm{id1}\right)$

Check the number of processed jobs.

 > $\mathrm{print}\left(\mathrm{tab}\left["curjob"\right]\right)$
 ${501}$ (6)

Check the results of one job.

 > $\mathrm{print}\left(\mathrm{tab}\left[233\right]\right)$
 ${108811}$ (7)
 > $\mathrm{print}\left(\mathrm{add}\left(i,i=1..2\cdot 233\right)\right)$
 ${108811}$ (8)