每个条件变量总是和一个互斥量相关联,条件本身是由互斥量保护的,线程在改变条件状态之间必须要锁住互斥量。条件变量相对于互斥量最大的优点在于允许线程以无竞争的方式等待条件的发生。当一个线程获得互斥锁后,发现自己需要等待某个条件变为真,如果是这样,该线程就可以等待在某个条件上,这样就不需要通过轮询的方式来判断添加,大大节省了CPU时间。
在互斥量一文中说过:互斥量是用于上锁,而不是用于等待;现在这句话可以加强为:互斥量是用于上锁,条件变量用于等待;条件变量声明为pthread_cond_t数据类型,在<bits/pthreadtypes.h>中有具体的定义。
<pthread.h>
int pthread_cond_init (pthread_cond_t
*cond,const pthread_condattr_t
*cond_attr) ;
int pthread_cond_destroy (pthread_cond_t
*cond);
两者的返回值都是:若成功则返回0,否则返回错误号
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_signal (pthread_cond_t
*cond);
/* 唤醒等待该条件的所有线程 */
int pthread_cond_broadcast (pthread_cond_t
*cond);
由于这两个操作时原子操作,这样就关闭了条件检查和线程进入睡眠等待条件改变这两个操作之间的时间通道,这样就不会错过任何条件的变化。当pthread_cond_wait()返回后,互斥量会再次被锁住。
2、pthread_cond_timedwait()函数和pthread_cond_wait()的工作方式相似,只是多了一个等待时间。等待时间的结构为struct timespec:
time_t tv_sec //Seconds.
long tv_nsec //Nanoseconds.
};
函数要求传入的时间值是 一个绝对值,不是相对值 ,例如,想要等待 3 分钟,必须先获得当前时间,然后加上
3 分钟。
要想获得当前系统时间的 timespec 值,没有直接可调用的函数,需要通过调用 gettimeofday 函数获取 timeval 结构,然后转换成 timespec 结构,转换公式就是:
timeSpec.tv_sec = timeVal.tv_sec;
timeSpec.tv_nsec = timeVal.tv_usec
* 1000;
所以要等待 3 分钟, timespec 时间结构的获得应该如下所示
:
struct timeval now;
struct timespec until;
gettimeofday(&now);//获得系统当前时间
//把时间从timeval结构转换成timespec结构
until.tv_sec = now.tv_sec;
until.tv_nsec = now.tv_usec
* 1000;
//增加min
until.tv_sec += 3
* 60;
如果时间到后,条件还没有发生,那么会返回 ETIMEDOUT 错误。
一般一个条件表达式都是在一个互斥量的保护下被检查的。当条件表达式未被满足时,线程将仍然阻塞在这个条件变量上。当另一个线程改变了条件的值并向条件变量发出信号时,等待在这个条件变量上的一个线程或者所有线程被唤醒,接着都试图再次占有相应的互斥量。
阻塞在条件变量上的线程被唤醒以后,直到pthread_cond_wait函数返回之前,条件的值都有可能发生变化。所以函数返回以后,在锁定相应的互斥量之前,必须重新测试条件变量。最后的测试方式是循环调用pthread_cond_wait函数,并把满足条件的表达式置为循环的终止条件。例如:
while (condition_is_false)
pthread_cond_wait();
pthread_mutex_unlock();
注意:pthread_cond_wait函数是退出点,如果在调用这个函数时,已有一个挂起的退出请求,且线程允许退出,这个线程将被终止并开始执行善后处理函数,而这时和条件变量相关的互斥量仍将处在锁定状态。(即pthread_cond_wait失败返回,但仍然获得互斥量,在清理函数注意解开该互斥量)
3、pthread_cond_signal() & pthread_cond_broadcast()
这两个函数都是用于向等待条件的线程发送唤醒信号,pthread_cond_signal()函数只会唤醒等待该条件的某个线程,pthread_cond_broadcast()会广播条件状态的改变,以唤醒等待该条件的所有线程。例如多个线程只读共享资源,这是可以将它们都唤醒。
这里要注意的是:一定要在改变条件状态后,再给线程发送信号。
考虑条件变量信号单播发送和广播发送的一种候选方式是坚持使用广播发送。只有在等待者代码编写确切,只有一个等待者需要唤醒,且唤醒哪个线程无所谓,那么此时为这种情况使用单播,所以其他情况下都必须使用广播发送。
唤醒阻塞在条件变量上的所有线程的顺序由调度策略决定,如果线程的调度策略是SCHED_OTHER类型的,系统将根据线程的优先级唤醒线程。
int pthread_condattr_init (pthread_condattr_t
*attr);
/* 销毁条件变量属性对象 */
int pthread_condattr_destroy (pthread_condattr_t
*attr);
/* 获取条件变量属性对象在进程间共享与否的标识 */
int pthread_condattr_getpshared (const pthread_condattr_t
*attr,int
*pshared);
/* 设置条件变量属性对象,标识在进程间共享与否 */
int pthread_condattr_setpshared (pthread_condattr_t
*attr, int pshared) ;
<stdio.h>
#include
<pthread.h>
#include
<unistd.h>
pthread_mutex_t count_lock;
pthread_cond_t count_nonzero;
unsigned count
= 0;
void
*decrement_count(void
*arg)
{
pthread_mutex_lock(&count_lock);
printf("decrement_count get count_lock\n");
while(count
== 0)
{
printf("decrement_count count == 0 \n");
printf("decrement_count before cond_wait \n");
pthread_cond_wait(&count_nonzero,
&count_lock);
printf("decrement_count after cond_wait \n");
printf("decrement_count count = %d \n",count);
}
count = count -
1;
pthread_mutex_unlock(&count_lock);
}
void
*increment_count(void
*arg)
{
pthread_mutex_lock(&count_lock);
printf("increment_count get count_lock \n");
if(count !=
0)
{
printf("increment_count before cond_signal \n");
pthread_cond_signal(&count_nonzero);
printf("increment_count after cond_signal \n");
}
count = count
+ 1;
printf("huangcheng \n");
printf("increment_count count = %d \n",count);
pthread_mutex_unlock(&count_lock);
}
int main(void)
{
pthread_t tid1, tid2;
pthread_mutex_init(&count_lock, NULL);
pthread_cond_init(&count_nonzero, NULL);
pthread_create(&tid1, NULL, decrement_count, NULL);
sleep(2);
pthread_create(&tid2, NULL, increment_count, NULL);
sleep(10);
pthread_exit(0);
return
0;
}
decrement_count get count_lock
decrement_count count == 0
decrement_count before cond_wait
increment_count get count_lock
increment_count before cond_signal
increment_count after cond_signal
huangcheng
increment_count count = 1
decrement_count after cond_wait
decrement_count count = 1
huangcheng@ubuntu:~$
说明:
等待线程
1.使用pthread_cond_wait前要先加锁
2.pthread_cond_wait内部会解锁,然后等待条件变量被其它线程激活
3.pthread_cond_wait被激活后会再自动加锁
激活线程:
1.加锁(和等待线程用同一个锁)
2.pthread_cond_signal发送信号
3.解锁
激活线程的上面三个操作在运行时间上都在等待线程的pthread_cond_wait函数内部。
即执行顺序:
等待线程->pthread_cond_wait内部会解锁,然后等待条件变量被其它线程激活
激活线程->加锁(和等待线程用同一个锁)
激活线程->pthread_cond_signal发送信号
激活线程->解锁
等待线程->pthread_cond_wait被激活后会再自动加锁
每个条件变量总是和一个互斥量相关联,条件本身是由互斥量保护的,线程在改变条件状态之间必须要锁住互斥量。条件变量相对于互斥量最大的优点在于允许线程以无竞争的方式等待条件的发生。当一个线程获得互斥锁后,发现自己需要等待某个条件变为真,如果是这样,该线程就可以等待在某个条件上,这样就不需要通过轮询的方式来判断添加,大大节省了CPU时间。
在互斥量一文中说过:互斥量是用于上锁,而不是用于等待;现在这句话可以加强为:互斥量是用于上锁,条件变量用于等待;条件变量声明为pthread_cond_t数据类型,在<bits/pthreadtypes.h>中有具体的定义。