在学习操作系统时候,其中关于线程的同步有四种方式:互斥锁,条件变量,读写锁,信号量,互斥锁,读写锁和信号量,这些都比较容易理解,关于条件变量,就不太好理解了,其中条件变量还注明了必须与互斥锁一起使用,接下来就以我的理解来说明一下,条件变量为何需要和互斥锁一起使用。
最好的理解就是从代码出发
首先贴一段最简单的互斥锁与条件变量一起使用的代码:
#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;
pthread_cond_t qready=PTHREAD_COND_INITIALIZER; //cond
pthread_mutex_t qlock=PTHREAD_MUTEX_INITIALIZER; //mutex
int x=10,y=20;
void *f1(void *arg)
{
cout<<"f1 start"<<endl;
pthread_mutex_lock(&qlock);
while(x<y)
{
pthread_cond_wait(&qready,&qlock);
}
pthread_mutex_unlock(&qlock);
sleep(3);
cout<<"f1 end"<<endl;
return 0;
}
void *f2(void *arg)
{
cout<<"f2 start"<<endl;
pthread_mutex_lock(&qlock);
x=20;
y=10;
cout<<"has a change,x="<<x<<" y="<<y<<endl;
pthread_mutex_unlock(&qlock);
if(x>y)
{
pthread_cond_signal(&qready);
}
cout<<"f2 end"<<endl;
return 0;
}
int main()
{
pthread_t tids[2];
int flag;
flag=pthread_create(&tids[0],NULL,f1,NULL);
if(flag)
{
cout<<"pthread 1 create error "<<endl;
return flag;
}
sleep(2);
flag=pthread_create(&tids[1],NULL,f2,NULL);
if(flag)
{
cout<<"pthread 2 create erro "<<endl;
return flag;
}
sleep(5);
return 0;
}
如果不理解pthread_cond_wait
这个函数,很难理解整个流程,先看输出结果
可以看到创建了两个线程,f1线程先执行,接下来,f1线程上锁,等待条件改变,进入阻塞状态,接下来,f2线程开始执行,f2线程上锁,改变了x和y变量,f2线程解锁,如果改变成功,则唤醒f1线程的阻塞,f2线程结束,f1线程解锁,f1线程结束。
这里有个问题:f1线程上锁后,并没有解锁,f2线程是如何拿到锁变量的,这里就需要详细展开 pthread_cond_wait
这个函数,这个函数具体做了什么事情:它首先将当前线程加入到唤醒队列,然后旋即解锁mutex,最后等待被唤醒。被唤醒后,又对mutex加锁,
我做了一幅图,可以更容易理解:
所以, pthread_cond_wait
在内部将互斥锁已经解开,这也就是为何pthread_cond_wait(&qready,&qlock)
里面也把锁变量作为参数,也就回答了为了条件变量为何要和互斥锁一起使用。