两个线程操作同一临界区时,通过互斥锁保护,若A线程已经加锁,B线程再加锁时候会被阻塞,直到A释放锁,B再获得锁运行,线程B必须不停的主动获得锁、检查条件、释放锁、再获得锁、再检查、再释放,一直到满足运行条件的时候才可以(而此过程中其他线程一直在等待该线程的结束),这种方式是比较消耗系统资源的。
条件变量同样是阻塞,还需要通知才能唤醒,线程被唤醒后,它将重新检查判断条件是否满足,如果还不满足,该线程就休眠了,仍阻塞在这里,等待条件满足后被唤醒,节省了线程不断运行浪费的资源。这个过程一般用while语句实现。
当线程B发现被锁定的变量不满足条件时会自动的释放锁并把自身置于等待状态,让出CPU的控制权给其它线程。
其它线程此时就有机会进行操作,当修改完成后再通知那些由于条件不满足而陷入等待状态的线程。这是一种通知模型的同步方式,大大的节省了CPU的计算资源,减少了线程之间的竞争,而且提高了线程之间的系统工作的效率。这种同步方式就是条件变量。
以上说明可能有点抽象,考虑这样的简单场景:通过伪代码说明。
A线程从队列中取元素,B线程往队列中存放元素。不考虑免锁的实现。需要一个mutex用来保护队列的一致性,避免两个线程同时操作队列破坏数据结构。
当队列为空的时候,A需要不断的探测队列状态:
while(1)
{
if(队列为空)
{
休眠10s
}
else
{
加锁
取元素
解锁
}
}
这就有一个问题,可能在刚进入休眠时,B放入元素了,但仍然需要休眠完整个10s的时间。造成不必要的延迟。当然如果不sleep也可以,但会造成不必要的CPU开销。
使用基于条件变量的事件通知唤醒机制,就可以避免这些问题。
一旦B放入元素完成后就执行pthread_cond_signal(),当前阻塞的线程就会立即被唤醒开始干活儿。
while(1)
{
pthread_mutex_lock();
pthread_cond_wait();
取元素;
pthread_mutex_unlock();
}
注意:条件变量都用互斥锁进行保护,条件变量状态的改变都应该先锁住互斥锁,
pthread_cond_wait()需要传入一个已经加锁的互斥锁,该函数把调用线程加入等待条件的调用列表中,然后释放mutex。
在条件满足从而离开pthread_cond_wait()时,重新对mutex加锁,这两个操作都是原子操作。
pthread_cond_signal通过条件变量cond发送消息,若多个消息在等待,它只唤醒一个。pthread_cond_broadcast可以唤醒所有。
调用pthread_cond_signal后要立刻释放互斥锁,因为pthread_cond_wait的最后一步是要将指定的互斥量重新锁住,如果pthread_cond_signal之后没有释放互斥锁,pthread_cond_wait仍然要阻塞。
pthread_mutex_lock();
pthread_cond_signal();
pthread_mutex_unlock();
条件变量为什么要与pthread_mutex一起使用呢?这是为了应对线程1在调用pthread_cond_wait()但线程1还没有进入wait cond的状态的时候,此时线程2调用了cond_singal的情况。 如果不用mutex锁的话,这个cond_singal就丢失了。加了锁的情况是,线程2必须等到mutex被释放(也就是 pthread_cod_wait()释放锁并进入wait_cond状态,此时线程2上锁)的时候才能调用cond_singal。
示例代码:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
int done = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
void thread_exit()
{
pthread_mutex_lock(&mutex);
done = 1;
pthread_cond_signal(&cond);
sleep(10);
printf("1111111\n");
pthread_mutex_unlock(&mutex);
}
void *child(void *args)
{
printf("child\n");
thread_exit();
return NULL;
}
void thread_join()
{
pthread_mutex_lock(&mutex);
while(done == 0)
{
printf("0000000\n");
pthread_cond_wait(&cond, &mutex);
}
printf("2222222\n");
pthread_mutex_unlock(&mutex);
}
int main()
{
pthread_t p;
printf("parent begin\n");
pthread_create(&p, NULL, child, NULL);
thread_join();
printf("parend end\n");
pthread_join(p, NULL);
return 0;
}
相关文章:
https://www.cnblogs.com/wsw-seu/p/8036186.html
https://www.jb51.net/article/102764.htm
原文:https://blog.csdn.net/piaopiaopiaopiaopiao/article/details/87912014