////////////////////////////////////////////////////////////////////////////////////////////////////////
NAME
pthread_cond_init, pthread_cond_destroy, pthread_cond_signal,
pthread_cond_broadcast, pthread_cond_wait, pthread_cond_timedwait -
operations on conditions
SYNOPSIS
#include
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t
*cond_attr);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t
*mutex, const struct timespec *abstime);
int pthread_cond_destroy(pthread_cond_t *cond);
DESCRIPTION
A condition (short for ``condition variable'') is a synchronization
device that allows threads to suspend execution and relinquish the pro-
cessors until some predicate on shared data is satisfied. The basic
operations on conditions are: signal the condition (when the predicate
becomes true), and wait for the condition, suspending the thread execu-
tion until another thread signals the condition.
A condition variable must always be associated with a mutex, to avoid
the race condition where a thread prepares to wait on a condition vari-
able and another thread signals the condition just before the first
thread actually waits on it.
条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。
pthread_cond_init initializes the condition variable cond, using the
condition attributes specified in cond_attr, or default attributes if
cond_attr is NULL. The LinuxThreads implementation supports no
attributes for conditions, hence the cond_attr parameter is actually
ignored.
Variables of type pthread_cond_t can also be initialized statically,
using the constant PTHREAD_COND_INITIALIZER.
pthread_cond_signal restarts one of the threads that are waiting on the
condition variable cond. If no threads are waiting on cond, nothing
happens. If several threads are waiting on cond, exactly one is
restarted, but it is not specified which.
pthread_cond_broadcast restarts all the threads that are waiting on the
condition variable cond. Nothing happens if no threads are waiting on
cond.
pthread_cond_wait atomically unlocks the mutex (as per
pthread_unlock_mutex) and waits for the condition variable cond to be
signaled. The thread execution is suspended and does not consume any
CPU time until the condition variable is signaled. The mutex must be
locked by the calling thread on entrance to pthread_cond_wait. Before
returning to the calling thread, pthread_cond_wait re-acquires mutex
(as per pthread_lock_mutex).
Unlocking the mutex and suspending on the condition variable is done
atomically. Thus, if all threads always acquire the mutex before sig-
naling the condition, this guarantees that the condition cannot be sig-
naled (and thus ignored) between the time a thread locks the mutex and
the time it waits on the condition variable.
pthread_cond_timedwait atomically unlocks mutex and waits on cond, as
pthread_cond_wait does, but it also bounds the duration of the wait. If
cond has not been signaled within the amount of time specified by
abstime, the mutex mutex is re-acquired and pthread_cond_timedwait
returns the error ETIMEDOUT. The abstime parameter specifies an abso-
lute time, with the same origin as time(2) and gettimeofday(2): an
abstime of 0 corresponds to 00:00:00 GMT, January 1, 1970.
pthread_cond_destroy destroys a condition variable, freeing the
resources it might hold. No threads must be waiting on the condition
variable on entrance to pthread_cond_destroy. In the LinuxThreads
implementation, no resources are associated with condition variables,
thus pthread_cond_destroy actually does nothing except checking that
the condition has no waiting threads.
CANCELLATION
pthread_cond_wait and pthread_cond_timedwait are cancellation points.
If a thread is cancelled while suspended in one of these functions, the
thread immediately resumes execution, then locks again the mutex argu-
ment to pthread_cond_wait and pthread_cond_timedwait, and finally exe-
cutes the cancellation. Consequently, cleanup handlers are assured
that mutex is locked when they are called.
ASYNC-SIGNAL SAFETY
The condition functions are not async-signal safe, and should not be
called from a signal handler. In particular, calling pthread_cond_sig-
nal or pthread_cond_broadcast from a signal handler may deadlock the
calling thread.
RETURN VALUE
All condition variable functions return 0 on success and a non-zero
error code on error.
ERRORS
pthread_cond_init, pthread_cond_signal, pthread_cond_broadcast, and
pthread_cond_wait never return an error code.
The pthread_cond_timedwait function returns the following error codes
on error:
ETIMEDOUT
the condition variable was not signaled until the timeout
specified by abstime
EINTR pthread_cond_timedwait was interrupted by a signal
The pthread_cond_destroy function returns the following error code on
error:
EBUSY some threads are currently waiting on cond.
AUTHOR
Xavier Leroy
SEE ALSO
pthread_condattr_init(3), pthread_mutex_lock(3),
pthread_mutex_unlock(3), gettimeofday(2), nanosleep(2).
EXAMPLE
Consider two shared variables x and y, protected by the mutex mut, and
a condition variable cond that is to be signaled whenever x becomes
greater than y.
int x,y;
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
Waiting until x is greater than y is performed as follows:
pthread_mutex_lock(&mut);
while (x <= y) {
pthread_cond_wait(&cond, &mut);
}
/* operate on x and y */
pthread_mutex_unlock(&mut);
Modifications on x and y that may cause x to become greater than y
should signal the condition if needed:
pthread_mutex_lock(&mut);
/* modify x and y */
if (x > y) pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mut);
If it can be proved that at most one waiting thread needs to be waken
up (for instance, if there are only two threads communicating through x
and y), pthread_cond_signal can be used as a slightly more efficient
alternative to pthread_cond_broadcast. In doubt, use
pthread_cond_broadcast.
To wait for x to becomes greater than y with a timeout of 5 seconds,
do:
struct timeval now;
struct timespec timeout;
int retcode;
pthread_mutex_lock(&mut);
gettimeofday(&now);
timeout.tv_sec = now.tv_sec + 5;
timeout.tv_nsec = now.tv_usec * 1000;
retcode = 0;
while (x <= y && retcode != ETIMEDOUT) {
retcode = pthread_cond_timedwait(&cond, &mut, &timeout);
}
if (retcode == ETIMEDOUT) {
/* timeout occurred */
} else {
/* operate on x and y */
}
pthread_mutex_unlock(&mut);
(二)
Linux多线程中互斥锁和条件变量的使用
#include
#include
#include
#include
pthread_mutex_t count_lock;
pthread_cond_t count_nonzero;
unsigned count;
void *decrement_count(void *arg)
{
pthread_mutex_lock(&count_lock);
while (count == 0)
pthread_cond_wait(&count_nonzero, &count_lock);
count = count - 1;
printf("LINE:%d %d\n", __LINE__, count);
pthread_mutex_unlock(&count_lock);
pthread_exit(NULL);
}
void *increment_count(void *arg)
{
pthread_mutex_lock(&count_lock);
if (count == 0)
pthread_cond_signal(&count_nonzero);
count = count + 1;
printf("LINE:%d %d\n", __LINE__, count);
sleep(2);
pthread_mutex_unlock(&count_lock);
pthread_exit(NULL);
}
int main()
{
pthread_t id1;
pthread_mutex_init(&count_lock, NULL);
pthread_cond_init(&count_nonzero, NULL);
pthread_create(&id1, NULL, decrement_count, NULL);
increment_count(NULL);
pthread_join(id1, NULL);
return 0;
}
(三)
在unix环境下,用于线程同步的条件变量是很重要的,在学习过程中发现有两点需要注意的。
一是,在使用条件变量的同时,需要一个互斥变量。这是为什么?在unix环境高级编程这本书中说到了,互斥变量是用来保护条件变量的。条件变量又是用来保护其他数据结构的,这样就容易出现混淆。
为什么条件变量需要互斥变量来保护呢?条件变量按字面上说就是用来标志条件满不满足,要知道满足不,就需要判断,在判断满足后就可以执行相应的操作。这两个步骤不是原子操作的,在判断和执行这两步之间存在一个window time。有可能条件变量被改变了,这样就可能出错误,所以要确保这两步按原子操作进行, 为此就用到了一个互斥变量。
二是,在使用pthread_cond_wait(&m_cond, &m_mutex)来等待条件的时候都发生了什么?在编程中一般都是以这样的结构出现的:
pthread_mutex_lock(&m_mutex);
pthread_cond_wait(&m_cond, &m_mutex);
pthread_mutex_unlock(&m_mutex);
从这个结构上看, 是不是我们的互斥变量就一直被运行这段代码的线程持有,而其他线程就只能等待互斥变量,这样,当条件满足的时候,就只能有一个线程能从wait中唤醒?从这样来看,有很多问题是讲不起走的。如果真是这样的话,你觉得pthread_cond_broadcast(&m_cond)这个api还有存在的必要吗?
那么在pthread_cond_wait()这个api的调用时,发生了什么?先是自动把调用它的线程放入等待条件变量的list里,再自动unblock 互斥变量。这样看来,并不是调用线程一直持有互斥变量,其他线程还是可以持有的(在pthread_cond_wait()自动unblock互斥变量以后)。可能你会问,如果自动unblock互斥变量了,那么上面代码的最后一行应该如何解释?其实,这个问题出在pthread_cond_wait()返回的时候,返回时是带blocked的互斥变量返回的。也就是说,在返回时,它会自动reacquire互斥变量。这样就能很好的解释最后的一行了。
有了上面的这两点,应该对条件变量的使用有很好的认识了。下面还有一点要说明的是,使用pthread_cond_signal()和pthread_cond_broadcast()来signaling condition的位置。这里有两个位置(以pthread_cond_broadcast()为例):
位置1:
pthread_mutex_lock(&m_mutex);
pthread_cond_wait(&m_cond, &m_mutex);
pthread_cond_broadcast(&m_cond);
pthread_mutex_unlock(&m_mutex);
位置2:
pthread_mutex_lock(&m_mutex);
pthread_cond_wait(&m_cond, &m_mutex);
pthread_mutex_unlock(&m_mutex);
pthread_cond_broadcast(&m_cond);
上面两个位置到底哪个位置是对的?在unix环境高级编程这本书上说,这个两个位置都有自己的缺点。
放在位置1会出现这样一个问题,当广播signal出去的时候,由于互斥变量还没有unblock,由于pthread_cond_wait()是要带互斥变量返回的,这样就只有在unlock互斥变量后,其他的等待线程才能执行下去,那么情况就是在收到信号后马上执行,然后马上block,因为没有获得互斥变量,要等待执行pthread_cond_broadcast()的线程unlock互斥变量。
放在位置2会出现这样一个问题,在unlock 和 broadcast两个操作之间有一个window time。在这个期间,条件变量可能变了,如果在变了后你发送信号那肯定就不对了。
信号这个东西还真是不好学,一个条件变量都能搞出这么多东西出来,发这个贴的目的是希望更多的人能明白这个东西,从而提高效率和节约时间。上面的言论是本人原创,可以自由转载,但希望能注明出处。