1、条件变量
条件变量是线程另一可用的同步机制。条件变量给多个线程提供了一个会合的场所。条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生。
条件本身是由互斥量保护的。线程在改变条件状态前必须首先锁住互斥量,其他线程在获得互斥量之前不会察觉到这种改变,因为必须锁定互斥量以后才能计算条件。
条件变量使用之前必须首先初始化,pthread_cond_t数据类型代表的条件变量可以用两种方式进行初始化,可以把常量PTHREAD_COND_INITIALIZER赋给静态分配的条件变量,但是如果条件变量是动态分配的,可以使用pthread_cond_destroy函数对条件变量进行去除初始化(deinitialize)。
使用条件变量可以以原子方式阻塞线程,直到某个特定条件为真为止。条件变量始终与互斥锁一起使用。对条件的测试是在互斥锁(互斥)的保护下进行的。
如果条件为假,线程通常会基于条件变量阻塞,并以原子方式释放等待条件变化的互斥锁。如果另一个线程更改了条件,该线程可能会向相关的条件变量发出信号,从而使一个或多个等待的线程执行以下操作:
- 唤醒
- 再次获取互斥锁
- 重新评估条件
在以下情况下,条件变量可用于在进程之间同步线程:
- 线程是在可以写入的内存中分配的
- 内存由协作进程共享
2、创建及销毁条件变量
#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);
// 返回:若成功返回0,否则返回错误编号
除非需要创建一个非默认属性的条件变量,否则pthread_cont_init函数的attr参数可以设置为NULL。
3、等待
#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, cond struct timespec *restrict timeout);
// 返回:若成功返回0,否则返回错误编号
pthread_cond_wait等待条件变为真。如果在给定的时间内条件不能满足,那么会生成一个代表一个出错码的返回变量。传递给pthread_cond_wait的互斥量对条件进行保护,调用者把锁住的互斥量传给函数。函数把调用线程放到等待条件的线程列表上,然后对互斥量解锁,这两个操作都是原子操作。这样就关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道,这样线程就不会错过条件的任何变化。pthread_cond_wait返回时,互斥量再次被锁住。
为什么条件变量始终与互斥锁一起使用,对条件的测试是在互斥锁(互斥)的保护下进行的呢?因为“某个特性条件”通常是在多个线程之间共享的某个变量。互斥锁允许这个变量可以在不同的线程中设置和检测。
默认情况下面,阻塞的线程会一直等待,知道某个条件变量为真。如果想设置最大的阻塞时间可以调用:
int pthread_cond_timedwait (pthread_cond_t * cptr, pthread_mutex_t *mptr, const struct timespec *abstime);
如果时间到了,条件变量还没有为真,仍然返回,返回值为ETIME。
4、触发
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
// 返回:若成功返回0,否则返回错误编号
这两个函数可以用于通知线程条件已经满足。
注意一定要在改变条件状态以后再给线程发信号。
通常,pthread_cond_wait只是唤醒等待某个条件变量的一个线程。如果需要唤醒所有等待某个条件变量的线程,需要调用:
int pthread_cond_broadcast (pthread_cond_t * cond); //广播
5、案例
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
int g_data = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
void *func1(void *arg)
{
printf("t1:%ld pthread is creat\n",(unsigned long)pthread_self());
printf("t1:param is %d\n", *((int *)arg));
while(1){
pthread_cond_wait(&cond,&mutex);
printf("t1 run==================\n");
printf("t1: %d\n",g_data);
g_data = 0;
sleep(1);
}
}
void *func2(void *arg)
{
printf("t2:%ld pthread is creat\n",(unsigned long)pthread_self());
printf("t2:param is %d\n", *((int *)arg));
while(1){
printf("t2: %d\n",g_data);
pthread_mutex_lock(&mutex);
g_data++;
if(g_data == 3){
pthread_cond_signal(&cond);
}
pthread_mutex_unlock(&mutex);
sleep(1);
}
}
int main()
{
int param = 100;
pthread_t t1;
pthread_t t2;
// pthread_mutex_init(&mutex,NULL);
// pthread_cond_init(&cond,NULL); // 动态初始化条件变量
pthread_create(&t1, NULL, func1, (void *)¶m);
pthread_create(&t2, NULL, func2, (void *)¶m);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
运行结果:
CLC@Embed_Learn:~/thread$ ./a.out
t1:140287221950208 pthread is creat
t1:param is 100
t2:140287213557504 pthread is creat
t2:param is 100
t2: 0
t2: 1
t2: 2
t1 run==================
t1: 3
t2: 0
t2: 1
t2: 2
t1 run==================
t1: 3
t2: 0
t2: 1
6、初始化补充
在使用互斥变量、条件变量前都必须进行初始化,可以分别置为常量PTHREAD_MUTEX_INITIALIZER(只对静态分配的互斥量)、PTHREAD_COND_INITIALIZER(只对静态分配的条件变量),也可以通过调用pthread_mutex_init、pthread_cond_init函数进行初始化。如果动态地分配互斥量、条件变量(例如通过调用malloc函数),那么在释放内存前需要调用pthread_mutex_destroy,条件变量可以使用pthread_cond_destroy函数对条件变量进行去除初始化(deinitialize)。
动态初始化:
pthread_mutex_t mutex;
pthread_cond_t cond;主函数中:
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
使用宏进行初始化(静态初始化):
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;