一、条件变量基本原理
条件变量可以使线程睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待“条件变量的条件成立”而挂起;另一个线程使“条件成立”(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。
二、条件变量操作流程
1.定义互斥锁、条件变量;
2.初始化互斥锁、条件变量;
3.创建线程;
4.条件不成立,线程进入睡眠状态;
5.另一线程在条件成立是唤醒睡眠的线程(可能不止一个线程);
6.线程退出,释放资源(销毁互斥锁、条件变量)。
三、条件变量基本操作函数
互斥锁基本操作函数如下表所示:
1.初始化条件变量
在使用条件变量前,需要定义该条件变量(全局变量),定义条件变量代码如下:
pthread_cond_t condtion
pthread_cond_init()
函数用来初始化条件变量,其函数申明如下:
int pthread_cond_init(pthread_cond_t* cond, pthread_condattr_t* cond_attr);
第1个参数cond指向要初始化的条件变量指针;
第2个参数cond_attr指向属性对象的指针,该属性对象定义要初始化的条件变量的特性,如果该指针为NULL,则使用默认的属性。下面是一段初始化条件变量的代码:
pthread_cond_t cv;
pthread_condattr_t cattr;
int ret;
ret = pthread_cond_init(&cv, NULL); // 使用默认属性初始化条件变量
ret = pthread_cond_init(&cv, &cattr); // 使用自定义属性初始化条件变量
函数执行成功时返回0。否则,返回错误编码号以指明错误。
2.等待条件变量
pthread_cond_wait()
函数阻塞等待某个条件变量,其函数声明如下:
int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex);
第1个参数cond是指向要等待的条件变量的指针;
第2个参数mutex是指向与条件变量cond关联的互斥锁的指针。
pthread_cond_timedwait()
函数阻塞等待某个条件变量,其函数声明如下:
int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex, struct timespec* abstime);
第1个参数cond是指向要等待的条件变量的指针;
第2个参数mutex是指向与条件变量cond关联的互斥锁的指针。
第3个参数abstime是等待过期的绝对时间,该时间为从1970-1-1:0:0:0以来的秒数,即为一个绝对时间。该数据结构声明如下:
struct timespec {
long ts_sec;
long ts_nsec;
};
两函数执行成功时返回0。否则,返回错误编码号以指明错误。
3.通知等待条件变量的线程
pthread_cond_signal()
函数用来通知等待条件变量的第一个线程。pthread_cond_broadcast()
函数用来通知等待条件变量的所以线程。两函数的声明如下:
int pthread_cond_signal(pthread_cond_t* cond);
int pthread_cond_broadcast(pthread_cond_t* cond);
参数cond是指向要通知或者广播的条件变量的指针。
两函数执行成功时返回0。否则,返回错误编码号以指明错误。
4.条件变量销毁
pthread_cond_destroy()
函数用来销毁条件变量,其函数申明如下:
int pthread_cond_destroy(pthread_cond_t* cond);
参数cond指向要销毁的条件变量的指针。
函数执行成功时返回0。否则,返回错误编码号以指明错误。
四、条件变量常规操作
下面给出条件变量常规操作:
#include <pthread.h>
static pthread_mutex_t mutex; /*互斥锁*/
static pthread_cond_t cond; /*条件变量*/
/**
* 初始化条件变量
*/
void init_cond(void)
{
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
return;
}
/**
* 销毁条件变量
*/
void destroy_cond(void)
{
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return;
}
/**
* 获取条件变量,使当前线程进入睡眠状态
*/
void get_cond(void)
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex); /*等待*/
pthread_mutex_unlock(&mutex);
return;
}
/**
* 唤醒等待条件变量进入睡眠的线程
*/
void wakeup_cond(void)
{
pthread_mutex_lock(&mutex); /*锁住互斥锁*/
pthread_cond_signal(&cond); /*条件改变,发送信号*/
pthread_mutex_unlock(&mutex); /*解锁互斥锁*/
return;
}
五、示例
初始状态有i=3, j=7
,A线程执行i++; j--;
操作。B线程只有在i == j
时执行do_somethind();
函数,下面通过条件变量来解决此问题:
#include <stdio.h>
#include <pthread.h>
int i = 3;
int j = 7;
static pthread_mutex_t mutex; /*互斥锁*/
static pthread_cond_t cond; /*条件变量*/
/**
* 初始化条件变量
*/
void init_cond(void)
{
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
return;
}
/**
* 释放条件变量
*/
void destroy_cond(void)
{
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return;
}
/**
* 获取条件变量,使当前线程进入睡眠状态
*/
void get_cond(void)
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex); /*等待*/
pthread_mutex_unlock(&mutex);
return;
}
/**
* 唤醒等待条件变量进入睡眠的线程
*/
void wakeup_cond(void)
{
pthread_mutex_lock(&mutex); /*锁住互斥锁*/
pthread_cond_signal(&cond); /*条件改变,发送信号*/
pthread_mutex_unlock(&mutex); /*解锁互斥锁*/
return;
}
void do_something()
{
printf("I am doing something ...\n");
}
void* pth_A(void* arg)
{
while (1) {
if (i == j) {
printf("pthread A wakeup_cond()\n");
wakeup_cond();
break;
}
i++;
j--;
sleep(1);
}
pthread_exit(0);
}
void* pth_B(void* arg)
{
while (1) {
if (i != j) {
printf("pthread B get_cond()\n");
get_cond();
}
if (i == j) {
do_something();
break;
}
}
pthread_exit(0);
}
int main()
{
pthread_t tida, tidb;
int ret;
// 初始化锁
init_cond();
ret = pthread_create(&tida, NULL, pth_A, NULL);
if (0 != ret) {
perror("create pthead error");
return -1;
}
ret = pthread_create(&tidb, NULL, pth_B, NULL);
if (0 != ret) {
perror("create pthead error");
return -1;
}
pthread_join(tida, NULL);
pthread_join(tidb, NULL);
// 销毁锁
destroy_cond();
return 0;
}
运行结果如下所示:
如上图所示线程B获得CPU资源时条件不成立,进入休眠状态,当线程A执行到i == j
时便唤醒B线程。B线程被唤醒时条件成立,便调用了函数do_something()
。