我们了解互斥量和条件变量之前,我们先来看一下为什么要有互斥量和条件变量这两个东西,了解为什么有这两东西之后,理解起来后面的东西就简单很多了!!!

先来看下面这段简单的代码:

int g_num = 0;void print(int id)
{for (int i = 0; i < 5; i++){++g_num;cout << "id = " << id << "==>" << g_num << endl;std::this_thread::sleep_for(std::chrono::seconds(1));}
}int main()
{thread tha(print, 0);thread thb(print, 1);tha.join();thb.join();return 0;
}

上述代码功能大致就是在线程tha和thb中运行函数print,每个线程对g_num进行加加一次,最后加出来的g_num的值应该是10,那么我们现在来看结果:

我们看到运行结果,为什么打印结果最后,按理来说两个线程各加五次,最后结果应该是10呀,怎么会是9呢?

如上图所示,是因为++这个运算符不是原子操作(不会被线程调度机制打断的操作),我们可以将g_num设置为原子数,改为atomic_int g_num = 0;

atomic_int g_num = 0; //将g_num设置为原子操作数
//atomic<int> g_num = 0;这个和上面是一样的 下面这行是模板化之后的void print(int id)
{for (int i = 0; i < 5; i++){++g_num;cout << "id = " << id << "==>" << g_num << endl;std::this_thread::sleep_for(std::chrono::seconds(1));}
}int main()
{thread tha(print, 0);thread thb(print, 1);tha.join();thb.join();return 0;
}

将g_num设置为原子操作数之后,在++阶段就不会被线程调度机制给打断,我们来看运行结果:

运行结果是我所期望的但是中间那块又出了一点小状况连着打着两个4,两个6,这种情况该怎么办呢?

下面就该说道我们的互斥锁了:

互斥锁:

        在上述代码中我们使用了共享资源----->全局量g_num,两个线程同时对g_num进行++操作,为了保护共享资源,在线程里也有这么一把锁——互斥锁(mutex),互斥锁是一种简单的加锁的方法来控制对共享资源的访问,互斥锁只有两种状态,即上锁( lock )和解锁( unlock )。

具体的互斥锁概念在我另外一篇博客中讲过Linux中互斥锁和条件变量的概念:Linux多线程的同步-----信号量和互斥锁_神厨小福贵!的博客-CSDN博客前面两篇给基本概念讲过了,大家有兴趣的可以去看一下:Linux多线程_神厨小福贵!的博客-CSDN博客进程和线程的区别有哪些呢?进程是资源分配的最小单位,线程是CPU调度的最小单位进程有自己的独立地址空间,线程共享进程中的地址空间进程的创建消耗资源大,线程的创建相对较小进程的切换开销大,线程的切换开销相对较小进程:程序执行的过程叫进程。线程:进程内部的一条执行序列或执行路径,一个进程可以包含多条线程(多线程)!每个进程最少有一个线程,例如下面代码:#include <stdio.h>int mahttps://blog.csdn.net/qq_45829112/article/details/121524904

来看下面代码:

int g_num = 0;
std::mutex mtx;  //创建锁对象void print(int id)
{for (int i = 0; i < 5; i++){mtx.lock();  //上锁++g_num;cout << "id = " << id << "==>" << g_num << endl;mtx.unlock(); //解锁std::this_thread::sleep_for(std::chrono::microseconds(500));}
}int main()
{thread tha(print, 0);thread thb(print, 1);tha.join();thb.join();return 0;
}

我们来看运行结果:符合我们最初的预期。

打开官方文档,可以看到

创建锁对象只有这个方法,拷贝构造被删除了 。

std::mutex::try_lock

对于互斥锁的lock和unlock我们都很熟悉了,下面来说一下std::mutex::try_lock这个成员函数!

try_lock字面意思就是说尝试上锁,如果上锁成功,返回true,上锁失败则返回false,但是如果上锁失败,他还是会接着往下运行,不会像lock哪运被阻塞在上锁那块,所以try_lock必须得在循环中使用:

int g_num = 0;
std::mutex mtx;void print(int id)
{for (int i = 0; i < 5; i++){mtx.try_lock();  //代码只是将lock换成了try_lock且没把try_lock扔在循环中执行++g_num;cout << "id = " << id << "==>" << g_num << endl;mtx.unlock();std::this_thread::sleep_for(std::chrono::microseconds(500));}
}int main()
{thread tha(print, 0);thread thb(print, 1);tha.join();thb.join();return 0;
}

我们来看运行结果:

 unlock of  unowned  mutex,这玩意思就是说你在给个没上锁的互斥锁解锁,所以报这错误,因此try_lock搁在普通语句中,会有很大的问题,现命我们演示一下将这玩意放到循环中去弄一边:

int g_num = 0;
std::mutex mtx;void print(int id)
{for (int i = 0; i < 5; i++){while (!mtx.try_lock())  //try_lock失败时为false 前面加了!,所以失败时为true 然后打印尝试加锁{                    //然后再次尝试加锁,只有加锁成功了,才能出这个while循环cout << "try  lock" << endl;}    ++g_num;cout << "id = " << id << "==>" << g_num << endl;mtx.unlock();std::this_thread::sleep_for(std::chrono::microseconds(500));}
}int main()
{thread tha(print, 0);thread thb(print, 1);tha.join();thb.join();return 0;
}

我们来看运行结果:

 运行结果符合我们的预期,但是try_lock这个函数有个不好处是太损耗资源了,当它加锁失败时,一直尝试加锁一直尝试加锁,损耗CPU资源。

条件变量:condition_variable

框住这三个函数较为重要,下面着重来说下面这三个函数: 这里顺便说一下,下面代码将会是条件变量和互斥锁的结合使用,至于为什么要将互斥锁和条件变量一起使用,原因就是互斥锁状态太单一了,而条件变量允许阻塞,接收信号量等刚好弥补了互斥锁的缺陷所以这些一起使用!!!

这三个函数呢,通过一个小实验来实现,通过多线程分别打印123一直到100:

std::mutex mtx;
std::condition_variable cv;
int isReady = 0;
const int n = 100;void print_A()
{std::unique_lock<mutex> lock(mtx);  //unique_lock相当于线程中的智能制造 自动解锁,不需要再unlockint i = 0;while (i < n){while (isReady != 0) {cv.wait(lock);//互斥锁和信号量一起使用  wait参数为锁对象}cout << "A" ;isReady = 1;++i;std::this_thread::sleep_for(std::chrono::microseconds(100));cv.notify_all(); //当isReady等于0时print_B 和 print_C 处于阻塞状态  //该函数就是唤醒所有等待的函数,然后通过isReady来进行判断要进行那个函数的运行}
}void print_B()
{std::unique_lock<mutex> lock(mtx);  //unique_lock相当于线程中的智能制造 自动解锁,不需要再unlockint i = 0;while (i < n){while (isReady != 1){cv.wait(lock);}cout << "B" ;isReady = 2;++i;std::this_thread::sleep_for(std::chrono::microseconds(100));cv.notify_all();}
}void print_C()
{std::unique_lock<mutex> lock(mtx);  //unique_lock相当于线程中的智能制造 自动解锁,不需要再unlockint i = 0;while (i < n){while (isReady != 2){cv.wait(lock);}cout << "C" ;isReady = 0;++i;std::this_thread::sleep_for(std::chrono::microseconds(100));cv.notify_all();}
}int main()
{thread tha(print_A);thread thb(print_B);thread thc(print_C);tha.join();thb.join();thc.join();return 0;
}

上面代码解析:

运行结果:

我们可以看到上述代码最后唤醒其他线程使用的是notify_all()函数,notify_all()函数作用就是环球其他阻塞的函数,然后因为isready这个数的存在,所以就会选择合适的线程来进行执行,如果我们使用notify_one()呢,先来说下notify_one()函数的作用是什么。notify_one()函数是唤醒其他线程(随机唤醒,这道题中不适合,因为如果打印完A之后唤醒了C线程那么就会一直阻塞在那块)

我们试一下notify_one()函数,可以发现这玩意确实会堵塞在那块:

【C++】多线程互斥锁、条件变量相关推荐

  1. 关于互斥锁,条件变量的内核源码解析

    一.解决问题和适用范围 主要是用来等待一个条件,这个条件可能需要另一个线程来满足这个条件.这个和我们平常适用的pthread_mutex_lock的最大不同在于后者保护的一般是一个代码段(也就是关键区 ...

  2. 信号量 互斥锁 条件变量的区别

    信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作(大家都在semtake的时候,就阻塞在哪里).而互斥锁是用在多线程多任务互斥的,一个线程占用了某 ...

  3. 信号量 互斥量 条件变量

    原文:https://blog.csdn.net/qq_32646795/article/details/78221005 本文打算写一些和锁有关的东西,谈一谈我对锁的原理和实现的理解,主要包含以下方 ...

  4. java多线程互斥锁_浅谈Java多线程互斥锁

    为了解决竞争条件带来的问题,我们可以对资源上锁.多个线程共同读写的资源称为共享资源,也叫临界资源.涉及操作临界资源的代码区域称为临界区(Critical Section).同一时刻,只能有一个线程进入 ...

  5. linux C++ 多线程使用pthread_cond 条件变量

    1. 背景 多线程中经常需要使用到锁(pthread_mutex_t)来完成多个线程之间的互斥操作. 但是互斥锁有一个明显到缺点: 只有两种状态,锁定和非锁定. 而条件变量则通过允许线程阻塞并等待另一 ...

  6. C++多线程编程(2) 条件变量与原子操作

    条件变量 条件变量是c++11 提供的另一种用于等待的同步机制,它能阻塞一个或者多个线程,直到收到另外一个线程发出的通知或者超时,才会唤醒当前阻塞的线程,条件变量需要和互斥量配合使用,C++11提供两 ...

  7. 【多线程】1.条件变量--std::condition_variable

    条件变量允许我们通过通知进而实现线程同步. 因此,您可以实现发送方/接收方或生产者/消费者之类的工作流. 在这样的工作流程中,接收者正在等待发送者的通知.如果接收者收到通知,它将继续工作. 1. st ...

  8. python多线程编程(5): 条件变量同步

    From: http://www.cnblogs.com/holbrook/archive/2012/03/13/2394811.html 互斥锁是最简单的线程同步机制,Python提供的Condit ...

  9. c++多线程基础4(条件变量)

    条件变量是允许多个线程相互交流的同步原语.它允许一定量的线程等待(可以定时)另一线程的提醒,然后再继续.条件变量始终关联到一个互斥 定义于头文件 <condition_variable> ...

最新文章

  1. Linear and Discrete Optimization - Week 1
  2. Java 多线程 之 银行ATM实例
  3. 图解SQL子查询实例
  4. Matlab之for循环语句
  5. 阿里云容器服务全面升级为 ACK Anywhere,让云的边界拓展至企业需要的每个场景
  6. google浏览器调试
  7. NeurIPS 2020 | AI编程:如何从复制粘贴走向推理合成(文末附论文及代码)
  8. CAD导出.eps格式图
  9. 163企业邮箱 端口服务器,163企业邮箱端口
  10. 串口转usb驱动c语言程序,电脑中USB转串口如何正确安装对应的驱动程序
  11. 网站备案靠谱吗_网上出现的代理备案可信?为什么可以4-7天备案成功?
  12. 敬伟PS教程:基础篇A
  13. Unity单机手游逆向破解思路(仅供学习参考,禁止用于非法行为)
  14. 第四周问题:Tu Hao's Problem
  15. Office小技巧|Excel表格输入身份证数字就变了乱码怎么办?
  16. codeforces 727E. Games on a CD
  17. 数值计算(三)-插值法(2)牛顿插值法
  18. 华擎主板bios设置图解_华擎主板bios设置_华擎主板bios设置图解_asrock主板bios设置...
  19. windows 安装 matplotlib 报错
  20. 【PV操作】买面包的叫号算法(存疑)

热门文章

  1. CVPR2020:点云弱监督三维语义分割的多路径区域挖掘
  2. 2021年大数据Spark(十七):Spark Core的RDD持久化
  3. f是一个python内部变量类型,Python基础变量类型——List浅析
  4. Python 判断本地python 本地版本2x or 3x
  5. Android RadioButton 修改选择框
  6. Failed to load AppCompat ActionBar with unknown error
  7. 003_如何学好英语?
  8. 二逼平衡树——树套树(线段树套Splay平衡树)
  9. 20190226-利用序列化完成小型记账程序
  10. python时间日期字符串各种