您当前的位置: 首页 >  ar

我什么都布吉岛

暂无认证

  • 3浏览

    0关注

    292博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

C++11线程库 (六) 条件变量 Condition variables

我什么都布吉岛 发布时间:2021-03-03 21:26:57 ,浏览量:3

一、什么是条件变量?

条件变量类(condition_variable)是一个同步原语,它可以在同一时间阻塞一个线程或者多个线程,直到其他线程改变了共享变量(条件)并通知。

primitive 原语,表达的是基础、基本的,是其他复杂应用的构建基础。

二、为什么需要条件变量?
  • 减少轮询从而提高效率。没有条件变量,CPU会浪费时间反复轮询某一个条件。条件变量出现使得线程可以在不满足条件进行休眠,将资源让给有需要的其他线程;
  • 线程之间的协调。两个线程之间执行可以变得有序,也就是一个线程执行完成后,另外一个线程才会执行;
  • 复杂同步条件。有时候一个线程不仅需要知道资源可达,还需要知道一些额外信息,比如说生产消费者中,缓冲队列是否满之类的操作;

线程进入阻塞可以是sleep,也可以是wait。sleep期间所持有的锁不会释放,到时见会自动唤醒,而wait期间将会释放锁,唤醒还需要额外的条件。前者更多的用在模拟操作延迟,后者用于多线程协作场景。

试想一个场景,两个线程th1和th2,他们分别执行这两个不同的变量,th1的继续执行必须等到th2完成其计算过程才可以继续执行,那么将会有两种策略:

  • 第一种,轮询th2是否执行标志位flag,th1种不断轮询标志位是否为true;
  • 第二种,th1和th2通过条件变量进行联系,th1根据th2是否通知决定其是等待还是继续执行;

第一种方法,最简单的实现方法就是:

void th1_fun()
{
	while(!flag)
	{
		std::this_thread::sleep_for(10ms);
	}
	//continue execute th1
}

检测到flag为false,线程进入休眠;检测到flag为true,退出while阻塞,继续执行,这个线程休眠是为了让其他线程有机会执行,也就是有机会让flag变成true,休眠时间如果过长,会导致响应延迟,如果休眠时间过短,cpu时间将会大部分浪费在判断flag是否改变。

第二种方法就是这里提到的条件变量,条件变量和轮询不一样的地方在于,他是用等待替代休眠的,这也就是说cpu不需要重复判断条件变量是否成立,而是通过另一个线程主动通知的方式告知无需继续等待。

三、std::condition_variable

头文件#include

3.1 条件变量是如何完成同步的?
  • 一个线程完成对数据的修改,另一个线程进行等待
  • 数据修改完成,通知另一个线程已经完成修改
  • 等待的线程收到通知,继续进行线程的执行

我们把对数据进行修改的线程称为通知线程,等待通知的线程称为等待线程。

3.2 通知端的具体工作
  • 获取一个std::mutex防止变量写冲突(通常是std::lock_guard);
  • 对变量值进行修改;
  • 对条件变量调用notify_one或者notify_all通知(通知的时候不一定要处于锁定);
3.3 等待端的具体工作
  • 获得一个std::unique_lock,这个互斥锁与通知端的相同;
  • 条件变量执行wait wait_forwait_until,等待操作将原子地完成释放互斥锁和挂起线程操作;
  • 当条件变量被通知时,超时或者虚假唤醒,线程将会被唤醒,互斥锁再次被获取,线程需要检查条件和假如时虚假唤醒则继续等待。

!!std::condition_variable 只可与 std::unique_lock 一同使用;此限制能让这种机制在一些平台取得最高的效率。当然如果你想使用与锁类型无关的条件变量,可以试试他的兄弟::std::condition_variable_any。注意等待端释放和挂起是原子操作,具体原因点这里。

3.4 成员函数

构造函数只有无参默认构造函数,无拷贝、无赋值。剩下的就是两类成员函数,一是通知方法,二是等待方法。

  • 通知方法:要么通知所有人notify_one,要么通知一个人notify_all

  • 等待方法:等待wait、等待一段时间wait_for和等待某个时刻wait_until。使用的方式就是“条件变量调用某种方法等待unique_lcok”

cond.wait(ulo);

调用wait方法导致当前线程阻塞直至条件变量被通知,或虚假唤醒发生,可选地循环直至满足某谓词。为了避免虚假唤醒,你可以使用谓词:

template
void wait( std::unique_lock& lock, Predicate pred );

等价于以下语句:

while (!pred()) {//不满足继续等待
    wait(lock);
}

这个谓词应该理解为解除阻塞条件。

四、条件变量实例

这个例子实现了按序打印并发线程:

class ConditionVarTest
{
    public:
    void printOne()
    {
        
        for(int i=0;i            
关注
打赏
1658157489
查看更多评论
0.0379s