条件变量相关 API
条件变量是线程另一可用的同步机制。条件变量给多个线程提供了一个会合的场所。条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生。
条件本身是由互斥量保护的。线程在改变条件状态前必须首先锁住互斥量,其他线程在获得互斥量之前不会察觉到这种改变,因为必须锁定互斥量以后才能计算条件。
在使用条件变量之前,必须先对它进行初始化。由pthread_cond_t 数据类型表示的条件变量可以用两种方式进行初始化,可以把常量 PTHREAD_COND_INITIALIZER 赋给静态分配的条件变量,但是如果条件变量是动态分配的,则需要使用pthread_cond_init 函数对它进行初始化。
在释放条件变量底层的内存空间之前,可以使用 pthread_cond_destroy 函数对条件变量进行反初始化(deinitialize)。
1. 创建及销毁条件变量
#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_cond_init函数的attr参数可以设置为NULL。<我们将在12.4.3节中讨论条件变量属性。—— UNIX 环境高级编程 >
我们使用pthread_cond_wait等待条件变量变为真。如果在给定的时间内条件不能满足,那么会生成一个返回错误码的变量。
2. 等待
#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 *rest99rict mutex,
cond struct timespec *restrict timeout);
返回:若成功返回0,否则返回错误编号
pthread_cond_wait 等待条件 变为真(即触发),如果在给定的时间内,条件不能满足,那么会生成一个 代表一个出错码的返回变量。
传递给 pthread_cond_wait 的互斥量对条件进行保护,调用者把锁住的互斥量传给函数,函数自动把调用线程放到等待条件的线程列表上,对互斥量解锁,这样,就关闭了条件检查 和 线程进入休眠状态等待条件改变 这两个操作之间的时间通道,这样线程就不会错过条件的任何变化。 pthread_cond_wait 返回时,互斥量再次被锁住。
pthread_cond_timedwait 函数的功能与 pthread_cond_wait 函数相似,只是多了一个超时(tsptr),超时值指定了我们愿意等待多长时间,它通过 timespec 结构指定。
3. 触发
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t * cond);
int pthread_cond_broadcast(pthread_cond_t * cond);
// 返回:若成功返回0,否则返回错误编号
这两个函数可以用于通知线程条件已经满足。 pthread_cond_signal 函数将唤醒等待该条件的某个线程,而pthread_cond_broadcast 函数将唤醒等待该条件的所有进程。
注意一定要在改变条件状态以后再给线程发信号。
上代码做例子吧,
这节课(线程最后一节)还是重做一遍吧,很多东西,我没有写在这篇博文中,(别问,问就是 废物,记得提醒我写,顺带抽我一巴掌)
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
// int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
// void *(*start_routine) (void *), void *arg);
pthread_cond_t cond;
pthread_mutex_t mutex;
// 创建一个锁的全局变量
int g_data = 0;
void * funcl1(void * arg){
printf("this is pthread t1, t1 的 ID 为 : %ld \n",pthread_self());
//errno 是记录系统的最后一次错误代码。代码是一个int型的值,在errno.h中定义
。查看错误代码errno是调试程序的一个重要方法。当linux C api函数发生异常时,一般会将errno变量(需include errno.h)赋一个整数值,不同的值表示不同的含义,可以通过查看该值推>测出错的原因。在实际编程中用这一招解决了不少原本看来莫名其妙的问题。
printf("t1's 参数为 %d \n",*(int *)arg);
while(1){
pthread_cond_wait(&cond,&mutex); // 首先要创建一个 条件变量
// 我们想要让 线程1 来判断 g_data 是否等于1 如果等于,那么就打印这
一句话。
if(g_data == 3){
printf("g_data == 3; t1 quit ===============\n");
}
printf("t1 = %d \n",g_data);
g_data = 0;
sleep(1);
}
}
void * funcl2(void * arg){
printf("this is pthread t2, t2 的 ID 为 : %ld \n",pthread_self());
//errno 是记录系统的最后一次错误代码。代码是一个int型的值,在errno.h中定
printf("t2's 参数为 %d \n",*(int *)arg);
while(1){
printf("t2 : %d \n",g_data);
pthread_mutex_lock(&mutex);
g_data++;
pthread_mutex_unlock(&mutex);
if(g_data == 3){
pthread_cond_signal(&cond);
}
pthread_mutex_unlock(&mutex);
sleep(1);
}
}
int main(){
int ret;
pthread_t t1;
pthread_t t2;
int * arg;
*arg = 100;
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
ret = pthread_create(&t1,NULL,funcl1,(void *)arg);
if(ret == 0){
printf("创建线程1 成功 \n");
}
ret = pthread_create(&t2,NULL,funcl2,(void *)arg);
if(ret == 0){
printf("创建线程2 成功 \n");
}
printf("main : %ld \n",pthread_self());
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}