关于互斥量std::mutex的总结

互斥量用于组成代码的临界区。C++的多线程模型是基于内存的,或者说是基于代码片段的,这和我们操作系统学习的临界区概念基本一致,但是与Golang不同,Golang是基于消息模型的。

一个std::mutexlock()unlock()之间的代码片段组成一个临界区,这个临界区内部同时最多只能有一个线程进行访问,可以理解为这个片段内部的代码是受到保护的,不会被多线程同时访问造成不可预知的问题。

std::mutex mtx;
mtx.lock();
// 这里的代码,同时最多只能有一个线程进行访问
mtx.unlokc();

当一个线程获取到一个std::mutex并且调用lock()后,必须由同一个线程调用unlock()操作,因此一定要注意两个函数成对出现,否则会造成死锁。

当一个线程获取一个std::mutex并调用lock()函数后,其他线程的调用会失败。可以使用try_lock进行判别,具体细节参考:https://en.cppreference.com/w/cpp/thread/mutex

关于std::lock_guardstd::unique_lock的总结

std::lock_guard比较好理解,因为调用mutex需要时刻记着解锁,所用这个类封装了一系列的操作,在一个模块中构造了一个std::lock_guard后,相当于对该结构块加锁,当线程离开结构块后,std::lock_guard自动析构,相当于解锁。它的结构简单、速度快,但是功能比较少。

std::unique_lock是对std::lock_guard功能的一个拓展,功能更多,但是速度会慢一些,具体参照:https://en.cppreference.com/w/cpp/thread/unique_lock

关于条件变量std::condition_variable的总结

这个相当于操作系统中的waitsignal原语操作,需要结合一个std::unique_lock组成的临界区共同完成功能。wait & signal原语操作最典型的特点是 “阻塞自己,唤醒别人”。可以这么理解,如果当前满足特定条件不满足,那么就不能进入临界区,当前线程阻塞。当其他线程处理完后,使得条件满足了,线程会唤醒那些处于阻塞状态的线程,使之重新进入。直接通过下面的代码来说明,经典的生产者和消费者问题。

注意只有一个互斥量的时候,唤醒顺序的问题,参照官网:https://en.cppreference.com/w/cpp/thread/condition_variable

在这里给出一个更加简洁的例子:

void worker_thread()
{// Wait until main() sends datastd::unique_lock<std::mutex> lk(m);cv.wait(lk, pred});// 这里执行工作代码,注意下面两个语句的顺序lk.unlock();cv.notify_one();
}

上述代码中,先执行lk.unlock(),说明当前线程放弃对临界区的所有权,此时再调用notify_one会唤醒其他线程来对临界区执行操作。如果先唤醒其他线程,则可能unlock未执行完毕,就有线程到临界区了,此时新来的线程又会阻塞了。

代码示例

下面代码总共是3个例子。第一个Application是模拟一个事件处理系统的,但是没有使用条件变量,自己实现了一下,第二个Application使用了条件变量。第三个ProduceAndConsume是典型的生产者和消费者模型。

未使用条件变量,仅仅借助循环的加载数据应用

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <chrono>
#include <random>
#include <cstdlib>const int MAXT = 1000;
std::uniform_int_distribution<int>dis(1, MAXT);
std::random_device rd;
std::mt19937 gen(rd());class Application {public:void mainTask() {std::cout << "Do some main task...\n";auto t = dis(gen);  // 随机时间模拟主线任务std::this_thread::sleep_for(std::chrono::milliseconds(t));std::cout << "Finish main task in " << t << " ms\n";mtx.lock();while (!m_bDataLoaded) {mtx.unlock();std::this_thread::sleep_for(std::chrono::microseconds(100));mtx.lock();}mtx.unlock();std::cout << "Get loaded data\n";}void loadData() {std::cout << "Loading data...\n";auto t = dis(gen);  // 随机时间模拟主线任务std::this_thread::sleep_for(std::chrono::milliseconds(t));std::lock_guard<std::mutex>lck(mtx);m_bDataLoaded = true;std::cout << "Finish loading data in " << t << " ms\n";}bool isDataLoaded()const {return m_bDataLoaded;}private:bool m_bDataLoaded{ false };std::mutex mtx;std::condition_variable m_convVar;
};int main() {Application app;std::thread t1(&Application::mainTask, &app);std::thread t2(&Application::loadData, &app);t1.join();t2.join();system("pause");return 0;
}

结果:

使用了条件变量的加载数据应用

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <chrono>
#include <random>
#include <cstdlib>const int MAXT = 1000;
std::uniform_int_distribution<int>dis(1, MAXT);
std::random_device rd;
std::mt19937 gen(rd());class Application {public:void mainTask() {std::cout << "Do some main task...\n";auto t = dis(gen);  // 随机时间模拟主线任务std::this_thread::sleep_for(std::chrono::milliseconds(t));std::cout << "Finish main task in " << t << " ms\n";std::unique_lock<std::mutex> lck(mtx);m_convVar.wait(lck, std::bind(&Application::isDataLoaded, this));std::cout << "Get loaded data\n";}void loadData() {std::cout << "Loading data...\n";auto t = dis(gen);  // 随机时间模拟加载数据任务std::this_thread::sleep_for(std::chrono::milliseconds(t));std::cout << "Finish loading task in " << t << " ms\n";std::unique_lock<std::mutex> lck(mtx);m_bDataLoaded = true;lck.unlock();   // 最好是添加上这一句,本例子无所谓m_convVar.notify_one();}bool isDataLoaded()const {return m_bDataLoaded;}private:bool m_bDataLoaded{ false };std::mutex mtx;std::condition_variable m_convVar;
};int main() {Application app;std::thread t1(&Application::mainTask, &app);std::thread t2(&Application::loadData, &app);t1.join();t2.join();system("pause");return 0;
}

运行结果:

生产者和消费者模型

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <random>
#include <functional>
#include <algorithm>
#include <queue>
#include <vector>
#include <cstdlib>const int MAXT = 1000;
const int MAXN = 5;
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<int>dis(MAXT / 10, MAXT);class ProducerAndConsumer {public:void prodece() {// 随机时间,模拟货物生产过程,生产过程本身不是在临界区std::this_thread::sleep_for(std::chrono::milliseconds(2 * dis(gen)));std::cout << "Prodece\n";std::unique_lock<std::mutex> lck(mtx);m_convPro.wait(lck, std::bind(&ProducerAndConsumer::notFull, this));m_qCargo.push(m_iCargoNum);++m_iCargoNum;m_convCon.notify_one();}void consume() {std::unique_lock<std::mutex> lck(mtx);m_convCon.wait(lck, std::bind(&ProducerAndConsumer::notEmpty, this));int n = m_qCargo.front();m_qCargo.pop();m_convPro.notify_one();lck.unlock();  // 一定要先解锁// 随机时间,模拟货物消费过程,消费过程本身不是在临界区!!!std::this_thread::sleep_for(std::chrono::milliseconds(dis(gen)));std::cout << "Consume\n";}inline bool notFull()const {return m_qCargo.size() < m_iMaxCargoNum;}inline bool notEmpty()const {return m_qCargo.size() > 0;}inline int getBufferSize()const {return m_qCargo.size();}inline int getCargoNum()const {return m_iCargoNum;}private:std::queue<int> m_qCargo;   // 货物队列int m_iMaxCargoNum{ MAXN }; // 最大容量int m_iCargoNum{ 0 };        // 货物总数std::mutex mtx;std::condition_variable m_convPro, m_convCon;
};int main() {ProducerAndConsumer pac;auto N = std::thread::hardware_concurrency();std::cout << "Thread num: " << N << std::endl;std::vector<std::thread>producerThreads;std::vector<std::thread>consumerThreads;for (int i = 0; i < N; ++i) {producerThreads.emplace_back(std::thread(&ProducerAndConsumer::prodece, &pac));consumerThreads.emplace_back(std::thread(&ProducerAndConsumer::consume, &pac));}std::for_each(producerThreads.begin(), producerThreads.end(),std::mem_fn(&std::thread::join));std::for_each(consumerThreads.begin(), consumerThreads.end(),std::mem_fn(&std::thread::join));std::cout << "Cargo in buffer: " << pac.getBufferSize() << std::endl;std::cout << "Cargo count: " << pac.getCargoNum() << std::endl;system("pause");return 0;
}

运行结果:

C++11多线程---互斥量、锁、条件变量的总结相关推荐

  1. C++11学习笔记-----互斥量以及条件变量的使用

    在多线程环境中,当多个线程同时访问共享资源时,由于操作系统CPU调度的缘故,经常会出现一个线程执行到一半突然切换到另一个线程的情况.以多个线程同时对一个共享变量做加法运算为例,自增的汇编指令大致如下, ...

  2. 互斥量、条件变量与pthread_cond_wait()函数的使用,详解(二)

    互斥量.条件变量与pthread_cond_wait()函数的使用,详解(二) 1.Linux"线程" 进程与线程之间是有区别的,不过linux内核只提供了轻量进程的支持,未实现线 ...

  3. c++ 互斥量和条件变量

    线程同步时会遇到互斥量和条件变量配合使用的情况,下面看一下C++版的. test.h #include <pthread.h> #include <iostream>class ...

  4. Linux下互斥量与条件变量详细解析

    1. 首先pthread_cond_wait 的定义是这样的 The pthread_cond_wait() and pthread_cond_timedwait() functions are us ...

  5. 并发编程(一): POSIX 使用互斥量和条件变量实现生产者/消费者问题

    boost的mutex,condition_variable非常好用.但是在Linux上,boost实际上做的是对pthread_mutex_t和pthread_cond_t的一系列的封装.因此通过对 ...

  6. c++11多线程编程同步——使用条件变量condition variable

    简述 在多线程编程中,当多个线程之间需要进行某些同步机制时,如某个线程的执行需要另一个线程完成后才能进行,可以使用条件变量. c++11提供的 condition_variable 类是一个同步原语, ...

  7. 信号灯文件锁linux线程,linux——线程同步(互斥量、条件变量、信号灯、文件锁)...

    一.说明 linux的线程同步涉及: 1.互斥量 2.条件变量 3.信号灯 4.文件读写锁 信号灯很多时候被称为信号量,但个人仍觉得叫做信号灯比较好,因为可以与"SYSTEM V IPC的信 ...

  8. 一个简单的互斥量与条件变量例子

    #include <pthread.h> #include <stdio.h> #include <stdlib.h> //互斥变量和条件变量静态初始化 pthre ...

  9. c++11 多线程编程(六)------条件变量(Condition Variable)

    互斥锁std::mutex是一种最常见的线程间同步的手段,但是在有些情况下不太高效. 假设想实现一个简单的消费者生产者模型,一个线程往队列中放入数据,一个线程往队列中取数据,取数据前需要判断一下队列中 ...

最新文章

  1. linux 修改java版本_Linux 有问必答:如何在 Linux 中改变默认的 Java 版本
  2. java包和继承的区别,子类和父类在同一个包中继承性
  3. Jvm垃圾回收器(终结篇)
  4. 源代码文档生成 Doxygen介绍(转载)
  5. 【Matlab】找到矩阵中每个连通域的最小值
  6. eclipse重置页面恢复到最初布局状态
  7. 会议交流 | IJCKG 2021:Keynotes released!欢迎注册参会
  8. 用汇编的眼光看C++(之嵌入汇编)
  9. 【NLP 算法岗】提前批暑期实习面(试)经(历)
  10. zipkin实战(python)
  11. “添加删除WIndows组件”中没有IIS时安装IIS方法
  12. 软件设计师教程 第5版 下载
  13. c 语言 sqlite,SQLite 的 C 语言编程
  14. Ring3与Ring0的通信
  15. Apache Flink_JZZ166_MBY
  16. MATLAB仪表表盘数字识别
  17. 大型医院HIS系统源码 优质源码 医院管理系统源码
  18. 二十三种设计模式 python实现
  19. Java Web 网络商城案例演示一、(环境搭建)
  20. 【邢不行|量化小讲堂系列24-Python量化入门】股票自动程序化下单交易 | 视频教程

热门文章

  1. 数据结构 1-0 绪论
  2. 【论文笔记】K-plet Recurrent Neural Networks for Sequential Recommendation
  3. 机器学习和传统编程有什么区别?✅
  4. ~~单调栈(数据结构)
  5. 如何在Ubuntu-16.04 / 18.04上为 RTX 2080 Ti GPU 安装Nvidia驱动和cuda-10.0
  6. sklearn模型保存
  7. QTextEdit显示中文乱码解决,中文GB2312转Unicode,QString、QByteArray 转换,16进制显示,toUtf8与toLocal8Bit区别
  8. 【Pytorch】nvidia-dali——一种加速数据增强的方法
  9. feign.RetryableException: Read timed out executing POST http://......
  10. Flume-概述-安装