简介:
条件变量可以在保证互斥的同时,实现进程之间的同步。(在条件变量等待的情况下,与其绑定的已经加锁的变量仍然可以被其他进程加锁)。条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足。条件变量通常是和互斥锁一起使用,因为条件的检测(条件检测通常是需要访问共享资源的) 是在互斥锁的保护下进行的,也就是说条件本身是由互斥锁保护的。
一、函数介绍
条件变量(pthread_cond_t)必须与锁(pthread_mutex_t)一起使用。
条件变量的API:
- pthread_cond_init
- pthread_cond_signal / pthread_cond_broadcast
- pthread_cond_wait / pthread_cond_timedwait
- pthread_cond_destroy
头文件:#include <pthread.h>
1、初始化
1.1 方法1:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
1.2 方法2:
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) //初始化
int pthread_cond_destroy(pthread_cond_t *cond); //销毁
参数:
- cond: 指向需要等待的条件变量;
- attr:状态属性,如果cond_attr为NULL,将会使用默认属性
返回值:函数调用成功返回 0,失败将返回一个非 0 值的错误码
2、通知和等待条件变量
2.1 等待条件变量
函数 | 功能 |
pthread_cond_wait | 线程等待信号触发,如果没有信号触发,无限期等待下去 |
pthread_cond_timedwait | 线程等待一定的时间,如果超时或有信号触发,线程唤醒 |
pthread_cond_wait / pthread_cond_timedwait 注意点:
- pthread_cond_wait 需配合互斥锁使用,调用 pthread_cond_wait 前需要先对互斥量 mutex 上锁;
- 在 pthread_cond_wait 函数内部,会首先对传入的 mutex 解锁。当等待的条件到来后,pthread_cond_wait 函数内部在返回前会去锁住传入的 mutex ;
1)pthread_cond_wait 函数
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
参数:
- cond: 指向需要等待的条件变量;
- mutex: 参数 mutex 是一个 pthread_mutex_t 类型指针,指向一个互斥锁对象;
返回值: 调用成功返回 0;失败将返回一个非 0 值的错误码;
2)pthread_cond_timedwait 函数
函数原型:
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime)
功能:用于等待条件变量,等待条件变量的同时可以设置等待超时;
参数:
- cond: 指向需要等待的条件变量;
- mutex: 参数 mutex 是一个 pthread_mutex_t 类型指针,指向一个互斥锁对象;
- abstime:超时时间,是一个绝对值,也就是距离1970-1-1 日的时间值。例如:当前时间为2000-1-1 1:00:00.000,我们想通过这个函数设置最大超时为2500ms,那么就需要设置abstime时间为2000-1-1 1:00:02.500;
返回值: 调用成功返回 0;失败将返回一个非 0 值的错误码;
说明:条件变量默认使用的时间是CLOCK_REALTIME。可通过gettimeofday 或 clock_gettime接口设置超时时间。
2.2 条件变量信号通知
int pthread_cond_signal(pthread_cond_t *cond) //唤醒一个线程
int pthread_cond_broadcast(pthread_cond_t *cond) //唤醒所有线程
返回值:成功返回0;
二、使用方法及实例
首先介绍pthread_cond_wait()函数使用方法
pthread_mutex_lock(&mutex); //加锁
pthread_cond_wait(&cond, &mutex); /*block-->unlock-->wait() return-->lock*/
pthread_mutex_unlock(&mutex); //解锁
有2点需要特别注意的地方:
- pthread_cond_wait 函数内部执行过程: 先unlock解锁mutex,然后进入休眠block等待条件变量堵塞,当有信号激活时,首先lock加锁mutex,然后执行。。。
- 等待条件有两种方式:条件等待pthread_cond_wait()和计时等待pthread_cond_timedwait(),当计时等待方式在给定时刻前条件没有满足,则返回ETIMEOUT。
1、pthread_cond_wait 使用实例
主线程 pthread_cond_wait 等待条件变量,另一个线程执行 pthread_cond_signal 发送条件变量信号通知主线程。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
static pthread_mutex_t mutex; //定义互斥锁
static pthread_cond_t cond; //定义条件变量
static int g_Value = 0;
static void *run_thread(void *arg)
{
int *pValue = (int *)arg;
while(1) {
pthread_mutex_lock(&mutex); //加锁
(*pValue)++; //+1
printf("Tx: %d(+1)\n", (*pValue));
pthread_cond_signal(&cond); //条件变量发送信号
pthread_mutex_unlock(&mutex); //解锁
sleep(2);
}
return 0;
}
int main(int argc, char *argv[])
{
pthread_t tid;
int ret;
printf("pthread_cond_t test\n");
/* 初始化互斥锁和条件变量 */
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
/* 创建新线程 */
ret = pthread_create(&tid, NULL, run_thread, &g_Value);
if (ret) {
fprintf(stderr, "pthread_create error: %s\n", strerror(ret));
printf("pthread_create failed!!!\n");
return -1;
}
while(1) {
pthread_mutex_lock(&mutex); //加锁
printf("Rx: wait...\n");
pthread_cond_wait(&cond, &mutex); //等待条件变量信号
printf("Rx: %d, run (-1)\n", g_Value);
g_Value--;
printf("Rx: result: %d\n", g_Value);
pthread_mutex_unlock(&mutex); //解锁
}
/* 销毁互斥锁和条件变量 */
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
return 0;
}
编译:$ gcc -o cond_test main.c -lpthread
运行结果:
$ ./cond_test
pthread_cond_t test
Rx: wait...
Tx: 1(+1)
Rx: 1, run (-1)
Rx: result: 0
Rx: wait... //主线程等待条件变量信号
Tx: 1(+1)
Rx: 1, run (-1)
Rx: result: 0
Rx: wait...
2、pthread_cond_timedwait 使用实例
pthread_cond_timedwait()用于等待一个条件变量,等待条件变量的同时可以设置等待超时。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
static pthread_mutex_t mutex; //定义互斥锁
static pthread_cond_t cond; //定义条件变量
static int g_Value = 0;
static void *run_thread(void *arg)
{
int *pValue = (int *)arg;
while(1) {
pthread_mutex_lock(&mutex); //加锁
(*pValue)++; //+1
printf("Tx: %d(+1)\n", (*pValue));
pthread_cond_signal(&cond); //条件变量发送信号
pthread_mutex_unlock(&mutex); //解锁
sleep(5);
}
return 0;
}
/* 设置超时绝对时间 */
static void setTimespecRelative(struct timespec *p_ts, long long msec)
{
struct timeval tv;
long long nsec = 0;
gettimeofday(&tv, NULL);
p_ts->tv_sec = tv.tv_sec + (msec / 1000);
nsec = (tv.tv_usec + (msec % 1000) * 1000L ) * 1000L; //处理纳秒进位
p_ts->tv_sec += (nsec / (1000 * 1000 * 1000));
p_ts->tv_nsec = nsec % (1000 * 1000 * 1000);
}
int main(int argc, char *argv[])
{
pthread_t tid;
int ret;
struct timespec ts;
int timeout = 0;
printf("pthread_cond_t test\n");
/* 初始化互斥锁和条件变量 */
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
/* 创建新线程 */
ret = pthread_create(&tid, NULL, run_thread, &g_Value);
if (ret) {
fprintf(stderr, "pthread_create error: %s\n", strerror(ret));
printf("pthread_create failed!!!\n");
return -1;
}
timeout = strtol(argv[1], NULL, 10);
while(1) {
pthread_mutex_lock(&mutex); //加锁
setTimespecRelative(&ts, timeout); //计算超时绝对时间
if (pthread_cond_timedwait(&cond, &mutex, &ts) == ETIMEDOUT) { //判断超时
printf("timeout(%d)\n", ETIMEDOUT);
}
g_Value--;
printf("Rx: result: %d\n", g_Value);
pthread_mutex_unlock(&mutex); //解锁
}
/* 销毁互斥锁和条件变量 */
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
return 0;
}
编译:$ gcc -o cond_timeout_test main.c -lpthread
运行结果:
可以看到,由于pthread_cond_timedwait 超时时间设的2秒,条件变量信号5秒发送一次,因此会出现timeout(110)超时。
$ ./cond_timeout_test 2000
pthread_cond_t test
Tx: 1(+1)
Rx: result: 0
timeout(110)
Rx: result: -1
timeout(110)
Rx: result: -2
Tx: -1(+1)
Rx: result: -2