c++ 学习之 多线程(八)条件变量condition_variable

condition_variable成员函数

condition_variable 是 c++11 提供的一个可以实现线程同步的类。下面来总结一下条件变量condition_variable的用法。

1. condition_variable::wait()

成员函数wait()需要与unique_lock搭配使用。wait()函数有两种调用方法。

(一)void wait (unique_lock& lck);

只有一个参数,传入一个unique_lock对象就可以了。 condition_variable对象调用wait ()函数后,该线程会休眠,并且释放unique_lock对象所占有的锁。直到同一对象调用 notify_one() 或者 notify_all() ,该线程会被唤醒,并且不断去获取unique_lock对象的锁,直到获取成功,函数返回。

(二)template <class Predicate> void wait (unique_lock& lck, Predicate pred);

需要两个参数,第一个参数传入一个unique_lock对象,第二个参数需要传入一个可调用对象(函数,lambda表达式等)。调用wait()函数时,会先拿到unique_lock的锁,接着判断可调用对象的返回值是否为true,若为true函数返回,否则释放掉unique_lock对象所占有的锁,然后该线程休眠,直到同一对象调用 notify_one() 或者 notify_all() ,该线程会被唤醒,然后不断去获取unique_lock对象的锁,直到获取成功再次判断可调用对象的返回值真假,为true函数返回,为false再次休眠,直到下一次唤醒。

2.condition_variable::notify_one()

condition_variable对象调用notify_one()来唤醒任意一个被该对象wait()函数阻塞的线程。如果没有线程被该对象wait()函数阻塞,就什么也不做。
注意,只能唤醒同一对象的wait函数,而且是任意一个,当有多个线程线程被该对象wait()函数阻塞时,无法确定是哪一个被唤醒。

3. condition_variable::notify_all()

和condition_variable::notify_one()功能一致,区别在于condition_variable::notify_all()可以唤醒所有的被该对象阻塞的线程。

还有成员函数 condition_variable::wait_for()和condition_variable::wait_until()这里不做详细解释。

condition_variable 的使用

首先看一下用双重检查模拟多线程的队列缓冲区维护。

#include<thread>
#include<mutex>
#include<queue>
#include<stdio.h>
using namespace std;
queue<int>qu;
condition_variable my_cond;
mutex m;
void PushQueue()
{
  for(int i=0;i<10000;i++)
  {
    unique_lock<mutex>m_lock(m);
    qu.push(i);
  }
}

void PopQueue()
{
   while(true)
   {
     if(!qu.empty())
     {
	  unique_lock<mutex>m_lock(m);
	  f(!qu.empty())
	   {
	        qu.pop();
	   }
      }
   }
}
int main()
{
 thread t1(PushQueue);
 thread t2(PopQueue);
 t1.join();
 t2.join();
}

在多线程的开发中,常常用到一种 生产者——消费者模式,将功能模块化,一个模块负责生产消息,一个模块负责处理数据,这样二者之间就要有一个缓冲区来作为桥梁。负责生产的模块将消息放入缓冲队列,负责处理的模块从缓冲队列中获取消息。
那么这个缓冲的队列作为二者的临界区,就必须保证其线程安全,使线程同步。以双重检查的机制来确保消费者模块可以从缓冲队列中安全的取到消息,无论是功能上还是效率上都是比较不错的。同样使用条件变量condition_variable,也可以实现这一功能。

#include<thread>
#include<mutex>
#include<queue>
#include<stdio.h>
using namespace std;
queue<int>qu;
condition_variable my_cond;
mutex m;
void PushQueue()
{
 for (int i = 0; i < 1000; i++)
 {
  unique_lock<mutex>m_lock(m);
  qu.push(i);
  m_lock.unlock();
  my_cond.notify_one();
    }
}
void PopQueue()
{
 
 while (true)
 {
  unique_lock<mutex>m_lock(m);
  my_cond.wait(m_lock, [=] {
   if (qu.empty())
    return false;
   return true;
   });
  qu.pop();
 }
}
int main()
{
 thread t1(PushQueue);
 thread t2(PopQueue);
 t1.join();
 t2.join();
}

最后还有一个需要注意的地方,只有当消费者线程被wait()阻塞时,生产者线程调用notify_one才会有用,所以当消费者线程被唤醒后,继续执行下面代码时,生产者调用的notify_one就不会发生效果,但是生产者线程每次都只会调用一次notify_one,所以消费者线程实际上被唤醒的次数不一定等于生产者调用notify_one的次数,每次唤醒只取一条会导致缓冲队列中的消息积压,造成消息无法及时处理,需要根据实际情况对这部分进行处理优化。

猜你喜欢

转载自blog.csdn.net/weixin_45074185/article/details/105274153