线程的同步
线程为了实现同步专门引入一种机制叫做条件变量,当然线程实现同步还有其它方式如互斥锁信号量等!
条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。 ------摘自百科
条件变量和互斥锁的使用非常类似,互斥锁的用法在我的博客中也有详细讲解(https://blog.csdn.net/weixin_48617416/article/details/119214739)
条件变量和互斥锁同样也是一个结构,类型是pthread_cond_t类型。
初始化条件变量
pthread_cont_t cond;
//方式一:静态方式初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
//方式二:动态方式初始化(使用pthread_cond_init函数)
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
//参数1:需要初始化的条件变量
//参数2:条件变量的属性
条件变量的激发
//激发方式一:激发一个
int pthread_cond_signal(pthread_cond_t *cond);
//只激发等待在该条件变量上等待队列中的第一个线程
//激发方式二:激发所有
int pthread_cond_broadcast(pthread_cond_t *cond);
//激发所有等待在该条件变量上的线程
条件变量的等待
//等待方式一:无条件等待(条件不成了,会一直等待)
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
//参数1:等待的条件变量
//参数2:互斥锁,当条件满足后,需要互斥访问临界资源所以需要加锁
//等待方式二:计时等待
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);
//参数1:等待的条件变量
//参数2:互斥锁
//参数3:等待条件变量成立的最长时间,如果等待的时间到了,那么等待函数就返回,不会一直等待
条件变量的销毁
int pthread_cond_destroy(pthread_cond_t *cond);
代码演示
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
struct Data {
int val;
pthread_mutex_t lock;
pthread_cond_t cond;
};
void *threadFunc(void *p)
{
struct Data *pData = (struct Data *)p;
//访问临界资源,需要相互访问
pthread_mutex_lock(&pData->lock);
//条件不满足,等待条件成立
if(0 == pData->val){
printf("条件不满足,等待条件满足\n");
pthread_cond_wait(&pData->cond, &pData->lock);
printf("条件成立,从条件变量上返回\n");
}
pthread_mutex_unlock(&pData->lock);
pthread_exit(NULL);
}
int main(int argc, char **argv)
{
struct Data data;
memset(&data, 0, sizeof(data));
data.val = 0;
//锁和条件变量初始化
pthread_mutex_init(&data.lock, NULL);
pthread_cond_init(&data.cond, NULL);
pthread_t thid;
pthread_create(&thid, NULL, threadFunc, &data);
//主线程让出时间片,让子线程先执行到pthread_cond_wait阻塞在条件变量上
//然后主线程使用signal激活该子线程
sleep(1);
pthread_mutex_lock(&data.lock);
//唤醒等待在条件变量上的线程
pthread_cond_signal(&data.cond);
printf("激发等待线程\n");
pthread_mutex_unlock(&data.lock);
pthread_join(thid, NULL);
//锁和条件变量的销毁
pthread_mutex_destroy(&data.lock);
pthread_cond_destroy(&data.cond);
return 0;
}
//运行结果:
条件不满足,等待条件满足
激发等待线程
条件成立,从条件变量上返回
上面是简单的条件变量的使用代码,看完代码后你因该是对这个代码有点疑惑才对,首先声明代码没有问题!想想看哪里有点疑惑?
疑惑点:首先应该放在锁上,子线程判断条件不满足,阻塞在pthread_cond_wait函数上,等待条件成立的信号;然后主线程sleep一秒之后,加锁,然后通知信号成立!想一下,子线程阻塞在pthread_cond_wait函数上之前是加锁了的,阻塞之后是没有解锁的,那么主线程是如何加锁成功的,并且调用了 pthread_cond_signal函数唤醒子线程的?
解惑:我们回头看一下pthread_cond_wait的原型
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
该函数的参数有一个条件变量,还有一个锁;条件变量很容易理解,那么这个锁呢?前面我讲原型的时候说了,为了访问临界资源需要加锁,那么我调用pthread_cond_wait函数前已经加锁了啊!那么我这个参数锁是干嘛的呢?其实pthread_cond_wait函数的源码实现中对该函数进行了功能划分(源码在glibc/nptl中可以查看),首先
- 第一步:加锁判断条件满足不满足,不满足调用pthread_cond_wait函数
- 第二步:由于在获取条件信息前加了锁,所以在pthread_cond_wait函数内部需要把锁释放,
- 第三步:睡眠
- 第四步:当收到条件满足的信号后,解除睡眠,
- 第五步:此时条件满足,说明资源可以使用,那么此时需要访问临界资源,所以需要加锁互斥访问临界资源!
- 第六步:函数返回!
通过我对pthread_cond_wait函数的分析你应该对我提出的问题有了全新的理解了!
pthread_cond_wait函数使用时需要的注意的是:该函数的锁参数,一定是你前面互斥加锁的时使用的锁变量,不然你咋在pthread_cond_wait函数内解锁呢!
本人能力有限,如有错误,望大佬不吝指出,原创不易,转载请注明出处!