Applying C - Condition Variables |
Written by Harry Fairhead | |||
Monday, 12 November 2018 | |||
Page 1 of 2 Condition variables seem to be hard to understand and tricky to use when you first meet them. However, they allow you to do things that are difficult to do any other way. This extract is from my book on C in an IoT context. Now available as a paperback or ebook from Amazon.Applying C For The IoT With Linux
Also see the companion book: Fundamental C <ASIN:1871962609> <ASIN:1871962617>
Condition VariablesA Join allows one thread to wait for another to complete but a condition variable allows any number of threads to wait for another thread to signal a condition. If you know a language and environment that supports events then you can think of condition variables as something like an event. When the condition is signaled then threads waiting on it are woken up. If you know the theory of concurrent programming then it might be helpful to know that a mutex plus a condition variable implements a monitor. You can declare a condition variable using: pthread_cond_t myConVar = PTHREAD_COND_INITIALIZER; You can also use the pthread_cond_init function and an condition attribute object to customize the condition variable. Condition variables are always used in conjunction with a mutex to control access to them. To wait on a condition variable you use: pthread_cond_wait(&myConVar , &mymutex); You need to lock the mutex before calling the function. This means that the thread might wait if another thread is trying to use the condition variable. Most of the time the mutex isn’t locked because the wait function puts the thread into a wait state and automatically unlocks the mutex. All of the threads waiting on the condition variable are suspended until another thread uses the signal function: pthread_cond_signal(&myConVar); In this case the mutex has to be locked before calling the function and unlocked after it. The signal function causes at least one of the threads that are waiting to be restarted. This vagueness of “at least one” is often a problem as it changes the behavior according to the system the program is running on. If you want to be sure to unblock all of the waiting threads then use: pthread_cond_broadcast(&myConVar); In either case multiple threads are started up one at a time and the thread that is started has the mutex in a locked state. There is one final problem. The specification says that spurious wakeups from wait states can occur. This means you cannot assume that when a thread is restarted from a wait some thread has necessrily signaled or broadcast. As a result you generally have to set up a global variable that indicates that a signal or broadcast has occurred and test it to make sure that this is a real wake up call. If it isn’t then you can wait on the condition variable again. You can call either function even if there are no thread waiting on the condition. The call only effects the threads currently waiting a the time the call is made – there is no memory that the condition has been signaled. This is a very simple mechanism but it can be used in many different ways. Let’s take a look at some of the most common. First Thread To FinishYou can easily wait for all threads to finish using join as was shown earlier in this chapter. However the related problem of waiting for the first thread of a group to finish is a harder problem but one easily solved using condition variables. The general idea is that each thread in the group is started and then the thread that wants to wait for the first thread to finish waits on the condition variable that they all share. Each thread calls cond_signal as soon as it has finished and this wakes up the waiting thread. As the other threads finish they too signal but by this time there are no waiting threads. First we need functions for two threads: void * threadA(void *p) { sleep(rand()%5); pthread_mutex_lock(&my_mutex); threadid=1; pthread_cond_signal(&myConVar); pthread_mutex_unlock(&my_mutex); } This function simply waits for a random number of seconds 0 to 4, then it locks the mutex, changes a shared global variable which is used to indicate which thread has finished. Notice that access to the global variable is also locked by tht mutex. Next it signals on the condition variable which starts the thread that is waiting and finally it unlocks the mutex. Notice the use of sleep which is a POSIX function that causes a thread to be suspended for the specified number of seconds. The ThreadB function is the same but it sets threadid to 2. |
|||
Last Updated ( Sunday, 09 June 2019 ) |