C++11 condition_variable条件变量的用法说明

目录
  • 1 什么是条件变量
  • 2 condition_variable类定义
    • 2.1 wait函数
  • 3 condition_variable用法
    • 3.1 资源修改线程步骤
    • 3.2 资源等待线程步骤
  • 4 代码示例
    • 4.1 无需notify场景
    • 4.2 正常应用场景1
    • 4.3 正常应用场景2

1 什么是条件变量

condition_variable是一个类,常和mutex搭配使用。

condition_variable类是一个同步原语,可用于阻塞一个线程或同时阻止多个线程,直到另一个线程修改共享变量并通知condition_variable。

防止多线程场景下,共享变量混乱。

理解条件变量要先理解三个概念:

  • 锁 (锁住共享变量,线程独占)
  • wait 等待 (等待通知条件变量,变化的共享变量是否满足条件)
  • notify 通知 (通知等待的条件变量,共享变量发送变化)

2 condition_variable类定义

2.1 wait函数

void wait( std::unique_lockstd::mutex& lock );
//Predicate是lambda表达式。
template< class Predicate >
void wait( std::unique_lockstd::mutex& lock, Predicate pred );
//以上二者都被notify_one())或notify_broadcast()唤醒,但是
//第二种方式是唤醒后也要满足Predicate的条件。
//如果不满足条件,继续解锁互斥量,然后让线程处于阻塞或等待状态。
//第二种等价于
while (!pred())
{
wait(lock);
}

3 condition_variable用法

condition_variable必定至少有两方,一方是资源修改线程,一方是资源等待线程。就跟打篮球一样,同时篮球只会在一个人手中,投篮后就释放了篮球所有权,其他方就会抢夺篮球所有权。

3.1 资源修改线程步骤

  • 获取一个mutex使用 std::unique_lock< std::mutex >
  • 保持锁定状态,修改共享变量
  • condition_variable对象执行notify_one或者notify_all(notify_one/notify_all执行前可以释放锁)

3.2 资源等待线程步骤

  • 获取一个mutex使用 std::unique_lock< std::mutex > unlock用于保护要修改的共享变量
  • 检查条件变量,

(1)条件变量满足,线程继续执行

(2)条件变量不满足,wait会释放unlock锁,并挂起线程。

  • 当notify通知条件变量、超时过期或发生虚假唤醒时,线程被唤醒,互斥锁unlock被原子地重新获取。然后,线程应该检查条件,如果唤醒是假的,则继续等待

4 代码示例

4.1 无需notify场景

当wait第一次执行是,条件已经满足,则程序不会阻塞(即无需notify),会直接向下执行。(仅为说明3.2 中第2点(1)的情况)

#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;
std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;

void worker_thread()
{
    std::cout << "3、worker_thread子线程开始执行"  << endl;
    // Wait until main() sends data
    std::unique_lock<std::mutex> lk(m);
    std::cout << "4、worker_thread子线程获取到锁,条件满足无需notify,不阻塞向下执行"  << endl;
    cv.wait(lk, []{return ready;});

    // after the wait, we own the lock.
    data += " after processing";
    // Send data back to main()
    processed = true;
    std::cout << "5、Worker thread signals data processing completed\n";

    // Manual unlocking is done before notifying, to avoid waking up
    // the waiting thread only to block again (see notify_one for details)
    lk.unlock();
    std::cout << "6、worker_thread子线程交出执行权限,主线程执行"  << endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(2000));

    cv.notify_one();
    std::cout << "9、worker_thread调用 notify_one"  << endl;
}
int main()
{
    std::thread worker(worker_thread);
    std::cout << "1、主线程开始执行"  << std::endl;
    data = "Example data";
    // send data to the worker thread
    {
        //std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        std::lock_guard<std::mutex> lk(m);
        ready = true;
    }
    std::cout << "2、锁已经释放了,主线程休眠,子线程执行"  << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    //cv.notify_one();
    {
        std::cout << "7、主线程data:" << data << endl;
        std::unique_lock<std::mutex> lk(m);
        std::cout << "8、主线程条件满足无需notify" << endl;
        cv.wait(lk, []{return processed;});
    }

    worker.join();
     std::cout << "10、主线程结束" << endl;
}

执行结果:

4.2 正常应用场景1

#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;
std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;

void worker_thread()
{
    std::cout << "3、worker_thread子线程开始执行"  << endl;
    // Wait until main() sends data
    std::unique_lock<std::mutex> lk(m);
    std::cout << "4、worker_thread子线程获取到锁,条件不满足,释放lk锁,子线程阻塞"  << endl;
    cv.wait(lk, []{return ready;});
    std::cout << "8、worker_thread子线程获取到锁,子线程继续执行"  << endl;
    // after the wait, we own the lock.
    data += " after processing";
    // Send data back to main()
    processed = true;
    std::cout << "9、Worker thread signals data processing completed\n";

    // Manual unlocking is done before notifying, to avoid waking up
    // the waiting thread only to block again (see notify_one for details)
    lk.unlock();
    std::this_thread::sleep_for(std::chrono::milliseconds(5000));
    std::cout << "10、worker_thread调用 notify_one通知主线程执行"  << endl;
    cv.notify_one();
}
int main()
{
    std::thread worker(worker_thread);
    std::cout << "1、主线程开始执行"  << std::endl;
    data = "Example data";
    // send data to the worker thread
    {
        std::cout << "2、主线程休眠,子线程进入执行"  << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        std::cout << "5、主线程结束休眠,主线程获取lk锁,进入执行"  << std::endl;
        std::lock_guard<std::mutex> lk(m);
        ready = true;

    }
    std::cout << "6、主线程释放lk,调用notify通知子线程"  << std::endl;
    cv.notify_one();
    {
        std::cout << "7、由于主线程的执行时钟周期未结束,继续执行主线程获取lk, wait检查条件不满足,释放锁" << endl;
        std::unique_lock<std::mutex> lk(m);
        cv.wait(lk, []{return processed;});
    }

    worker.join();
     std::cout << "11、主线程结束" << endl;
}

执行结果:

这里notify执行后不一定立即执行子线程,如果cpu执行时钟周期未结束,则主线程会继续执行. 所以7,8,9,10顺序可能变化参见4.3

同时4.1也会因为cpu时钟周期,执行顺序有所变动

4.3 正常应用场景2

#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;
std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;

void worker_thread()
{
    std::cout << "3、worker_thread子线程开始执行"  << endl;
    // Wait until main() sends data
    std::unique_lock<std::mutex> lk(m);
    std::cout << "4、worker_thread子线程获取到锁,条件不满足,释放lk锁,子线程阻塞"  << endl;
    cv.wait(lk, []{return ready;});
    std::cout << "8、worker_thread子线程获取到锁,子线程继续执行"  << endl;
    // after the wait, we own the lock.
    data += " after processing";
    // Send data back to main()
    processed = true;
    std::cout << "9、Worker thread signals data processing completed\n";

    // Manual unlocking is done before notifying, to avoid waking up
    // the waiting thread only to block again (see notify_one for details)
    lk.unlock();
    std::cout << "10、worker_thread调用 notify_one通知主线程执行"  << endl;
    cv.notify_one();
}
int main()
{
    std::thread worker(worker_thread);
    std::cout << "1、主线程开始执行"  << std::endl;
    data = "Example data";
    // send data to the worker thread
    {
        std::cout << "2、主线程休眠,子线程进入执行"  << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        std::cout << "5、主线程结束休眠,主线程获取lk锁,进入执行"  << std::endl;
        std::lock_guard<std::mutex> lk(m);
        ready = true;

    }
    std::cout << "6、主线程释放lk,调用notify通知子线程"  << std::endl;
    cv.notify_one();
    {
        for(int i = 0; i< 10000000; i++)
        {
            int j = i;
        }
        std::cout << "7、由于主线程的执行时钟周期未结束,继续执行主线程获取lk, wait检查条件不满足,释放锁" << endl;
        std::unique_lock<std::mutex> lk(m);
        cv.wait(lk, []{return processed;});
    }

    worker.join();
    std::cout << "11、主线程结束" << endl;
}

执行结果:

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • c++多线程为何要使用条件变量详解

    先看示例1: #include <iostream> #include <windows.h> #include <mutex> #include<deque> #include <thread> using namespace std; int nmax = 20; std::deque<int> m_que; std::mutex mymutex; //生产者 void producterex() { int i = 1; whi

  • 详解C++11中的线程锁和条件变量

    线程 std::thread类, 位于<thread>头文件,实现了线程操作.std::thread可以和普通函数和 lambda 表达式搭配使用.它还允许向线程的执行函数传递任意多参数. #include <thread> void func() { // do some work } int main() { std::thread t(func); t.join(); return 0; } 上面的例子中,t是一个线程实例,函数func()在该线程运行.调用join()函数是

  • C++多线程互斥锁和条件变量的详解

    目录 互斥锁: std::mutex::try_lock 条件变量:condition_variable 总结 我们了解互斥量和条件变量之前,我们先来看一下为什么要有互斥量和条件变量这两个东西,了解为什么有这两东西之后,理解起来后面的东西就简单很多了!!! 先来看下面这段简单的代码: int g_num = 0; void print(int id) { for (int i = 0; i < 5; i++) { ++g_num; cout << "id = " &l

  • C++多线程中的锁和条件变量使用教程

    在做多线程编程时,有两个场景我们都会遇到: 多线程访问共享资源,需要用到锁: 多线程间的状态同步,这个可用的机制很多,条件变量是广泛使用的一种. 今天我用一个简单的例子来给大家介绍下锁和条件变量的使用. 代码使用C++11 示例代码 #include <iostream> #include <mutex> #include <thread> #include <condition_variable> std::mutex g_mutex; // 用到的全局锁

  • C++11 condition_variable条件变量的用法说明

    目录 1 什么是条件变量 2 condition_variable类定义 2.1 wait函数 3 condition_variable用法 3.1 资源修改线程步骤 3.2 资源等待线程步骤 4 代码示例 4.1 无需notify场景 4.2 正常应用场景1 4.3 正常应用场景2 1 什么是条件变量 condition_variable是一个类,常和mutex搭配使用. condition_variable类是一个同步原语,可用于阻塞一个线程或同时阻止多个线程,直到另一个线程修改共享变量并通

  • C++中自定义sleep、条件变量sleep实例

    sleep的作用无需多说,几乎每种语言都提供了类似的函数,调用起来也很简单.sleep的作用无非是让程序等待若干时间,而为了达到这样的目的,其实有很多种方式,最简单的往往也是最粗暴的,我们就以下面这段代码来举例说明(注:本文提及的程序编译运行环境为Linux) 复制代码 代码如下: /* filename: test.cpp */  #include <stdio.h>  #include <unistd.h>  #include <pthread.h>  #inclu

  • go语言变量定义用法实例

    本文实例讲述了go语言变量定义用法.分享给大家供大家参考.具体如下: var语句定义了一个变量的列表:跟函数的参数列表一样,类型在后面. 复制代码 代码如下: package main import "fmt" var x, y, z int var c, python, java bool func main() {     fmt.Println(x, y, z, c, python, java) } 变量定义可以包含初始值,每个变量对应一个. 如果初始化是使用表达式,则可以省略类

  • 浅谈互斥锁为什么还要和条件变量配合使用

    mutex体现的是一种竞争,我离开了,通知你进来. cond体现的是一种协作,我准备好了,通知你开始吧. 互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定.而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起配合使用.使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化.一旦其他的某个线程改变了条件变量,他将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程.这些线程将重新锁定互斥锁并重新测试条件是否满足.

  • C#中static静态变量的用法实例

    本文实例讲述了C#中static静态变量的用法.分享给大家供大家参考.具体如下: 使用 static 修饰符声明属于类型本身而不是属于特定对象的静态成员static修饰符可用于类.字段.方法.属性.运算符.事件和构造函数,但不能用于索引器.析构函数或类以外的类型   静态全局变量 定义:在全局变量前,加上关键字 static 该变量就被定义成为了一个静态全局变量. 特点: ① .该变量在全局数据区分配内存. ② .初始化:如果不显式初始化,那么将被隐式初始化为0.   静态局部变量 定义:在局部

  • php动态绑定变量的用法

    本文实例讲述了php动态绑定变量的用法.分享给大家供大家参考.具体如下: private function bindVars($stmt,$params) { if ($params != null) { $types = ''; //initial sting with types foreach($params as $param) { //for each element, determine type and add if(is_int($param)) { $types .= 'i';

随机推荐