Threads Condition Variables

Condition variables are another synchronization mechanism available to threads. These
synchronization objects provide a place for threads to rendezvous. When used with
mutexes, condition variables allow threads to wait in a race-free way for arbitrary
conditions to occur.
The condition itself is protected by a mutex. A thread must first lock the mutex to
change the condition state. Other threads will not notice the change until they acquire
the mutex, because the mutex must be locked to be able to evaluate the condition.
Beforeacondition variable is used, it must first be initialized. A condition variable,
represented by the pthread_cond_t data type, can be initialized in two ways. We can
assign the constant PTHREAD_COND_INITIALIZER to a statically allocated condition
variable, but if the condition variable is allocated dynamically, we can use the
pthread_cond_init function to initialize it.
We can use the pthread_cond_destroy function to deinitialize a condition
variable before freeing its underlying memory.

#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);

Both return: 0 if OK, error number on failure

Unless you need to create a conditional variable with nondefault attributes, the attr
argument to pthread_cond_init can be set to NULL. We will discuss condition
variable attributes in Section 12.4.3.
We use pthread_cond_wait to wait for a condition to be true. A variant is
provided to return an error code if the condition hasn’t been satisfied in the specified
amount of time.

#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict tsptr);

Both return: 0 if OK, error number on failure

The mutex passed to pthread_cond_wait protects the condition. The caller
passes it locked to the function, which then atomically places the calling thread on the
list of threads waiting for the condition and unlocks the mutex. This closes the window
between the time that the condition is checked and the time that the thread goes to sleep
waiting for the condition to change, so that the thread doesn’t miss a change in the
condition. When pthread_cond_wait returns, the mutex is again locked.
The pthread_cond_timedwait function provides the same functionality as the
pthread_cond_wait function with the addition of the timeout (tsptr). The timeout
value specifies how long we are willing to wait expressed as a timespec structure.
Just as we saw in below code, we need to specify how long we are willing to wait
as an absolute time instead of a relative time. For example, suppose we are willing to
wait 3 minutes. Instead of translating 3 minutes into a timespec structure, we need to
translate now + 3 minutes into a timespec structure.
We can use the clock_gettime function to get the current time
expressed as a timespec structure. However, this function is not yet supported on all
platforms. Alternatively, we can use the gettimeofday function to get the current
time expressed as a timeval structure and translate it into a timespec structure. To
obtain the absolute time for the timeout value, we can use the following function
(assuming the maximum time blocked is expressed in minutes):

#include <sys/time.h>
#include <stdlib.h>
void maketimeout(struct timespec *tsp, long minutes)
{
	struct timeval now;
	/* get the current time */
	gettimeofday(&now, NULL);
	tsp->tv_sec = now.tv_sec;
	tsp->tv_nsec = now.tv_usec * 1000; /* usec to nsec */
	/* add the offset to get timeout value */
	tsp->tv_sec += minutes * 60;
}

If the timeout expires without the condition occurring, pthread_cond_timedwait
will reacquire the mutex and return the error ETIMEDOUT. When it returns from a
successful call to pthread_cond_wait or pthread_cond_timedwait, a thread
needs to reevaluate the condition, since another thread might have run and already
changed the condition.
There are two functions to notify threads that a condition has been satisfied. The
pthread_cond_signal function will wake up at least one thread waiting on a
condition, whereas the pthread_cond_broadcast function will wake up all threads
waiting on a condition.

The POSIX specification allows for implementations of pthread_cond_signal to wake up
more than one thread, to make the implementation simpler.

#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
Both return: 0 if OK, error number on failure

When we call pthread_cond_signal or pthread_cond_broadcast, we are
said to be signaling the thread or condition. We have to be careful to signal the threads
only after changing the state of the condition.

The example of how to use a condition variable and a mutex together to synchronize threads.
eg: pthread_cond_wait

#include <pthread.h>

struct msg {
struct msg *m_next;
/* ... more stuff here ... */
};

struct msg *workq;
pthread_cond_t qready = PTHREAD_COND_INITIALIZER;
pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER;
void
process_msg(void)
{
	struct msg *mp;
	for (;;) {
		pthread_mutex_lock(&qlock);
		while (workq == NULL)
		pthread_cond_wait(&qready, &qlock);
		mp = workq;
		workq = mp->m_next;
		pthread_mutex_unlock(&qlock);
		/* now process the message mp */
	}
}
void
enqueue_msg(struct msg *mp)
{
	pthread_mutex_lock(&qlock);
	mp->m_next = workq;
	workq = mp;
	pthread_mutex_unlock(&qlock);
	pthread_cond_signal(&qready);
}

eg: pthread_cond_timedwait

#include <pthread.h>
#include <sys/time.h>
#include <stdlib.h>

struct msg {
	struct msg *m_next;
	/* ... more stuff here ... */
};

struct msg *workq;
pthread_cond_t qready = PTHREAD_COND_INITIALIZER;
pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER;

void process_msg(void)
{
	struct msg *mp;
	struct timeval now;
	struct timespec time;
	
	for (;;) {
		pthread_mutex_lock(&qlock);
		while (workq == NULL) {
			/* get the current time */
			gettimeofday(&now, NULL);
			timet.v_sec = now.tv_sec;
			time.tv_nsec = now.tv_usec * 1000; /* usec to nsec */
			pthread_cond_timedwait(&qready, &qlock, &time);
		}
		mp = workq;
		workq = mp->m_next;
		pthread_mutex_unlock(&qlock);
		/* now process the message mp */
	}
}
void
enqueue_msg(struct msg *mp)
{
	pthread_mutex_lock(&qlock);
	mp->m_next = workq;
	workq = mp;
	pthread_mutex_unlock(&qlock);
	pthread_cond_signal(&qready);
}

This part of infomation come from the book of <Advanced Programming in the UNIX® Environment Third Edition> and also the code.
If you want to download the source code of Advanced Programming in the UNIX® Environment Third Edition, please referrence the link http://apuebook.com/code3e.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章