【多线程那些事儿】多线程的执行顺序如你预期吗?

[复制链接]
发表于 2022-9-27 16:21:55 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
一个简单的例子

先来看一个多线程的例子:
graph TB        begin(a)-->线程1(x = 1, m = y)        begin(x = 0, y = 0)-->线程2(y = 1, n = x)如图所示,我们将变量x和y初始化为0,然后在线程1中执行:
  1.         x = 1, m = y;
复制代码
同时在线程2中执行:
  1.         y = 1, n = x;
复制代码
当两个线程都执行结束以后,m和n的值分别是多少呢?
对于已经工作了n年、写过无数次并发程序的的我们来说,这还不是小case吗?让我们来分析一下,大概有三种情况:

  • 如果程序先执行了x = 1, m = y代码段,后执行了y = 1, n = x代码段,那么结果是m = 0, n = 1;
  • 如果程序先执行了y = 1, n = x代码段,后执行了x = 1, m = y代码段,那么结果是m = 1, n = 0;
  • 如果程序的执行顺序先是 x = 1, y = 1, 后执行m = y, n = x, 那么结果是m = 1, n = 1;
所以(m, n)的组合一共有3种情况,分别是(0, 1), (1, 0)和(1, 1)。
那有没有可能程序执行结束后,(m, n)的值是(0, 0)呢?嗯...我们又仔细的回顾了一下自己的分析过程:在m和n被赋值的时候,x = 1和y = 1至少有一条语句被执行了...没有问题,那应该就不会出现m和n都是0的情况。
诡异的输出结果

不过人在江湖上混,还是要严谨一点。好在这代码逻辑也不复杂,那就写一段简单的程序来验证下吧:
  1. #include <iostream>
  2. #include <thread>
  3. using namespace std;
  4. int x = 0, y = 0, m = 0, n = 0;
  5. int main()
  6. {
  7.         while (1) {
  8.                 x = y = 0;
  9.                 thread t1([&]() { x = 1; m = y; });
  10.                 thread t2([&]() { y = 1; n = x; });
  11.                 t1.join(); t2.join();
  12.                 if (m == 0 && n == 0) {
  13.                         cout << " m == 0 && n == 0 ? impossible!\n";
  14.                 }
  15.         }
  16.         return 0;
  17. }
复制代码
不过在现代C++中,要做这样的事情就简单多了。C++11引入了原子类型(atomic),同时规定了6种内存执行顺序:

  • memory_order_relaxed: 松散的,在保证原子性的前提下,允许进行任务的重新排序;
  • memory_order_release: 代码中这条语句前的所有读写操作, 不允许被重排到这个操作之后;
  • memory_order_acquire: 代码中这条语句后的所有读写操作,不允许被重排到这个操作之前;
  • memory_order_consume: 代码中这条语句后所有与这块内存相关的读写操作,不允许被重排到这个操作之前;注意,这个类型已不建议被使用;
  • memory_order_acq_rel: 对读取和写入施加acquire-release语义,无法被重排;
  • memory_order_seq_cst: 顺序一致性,如果是写入就是release语义,如果是读取是acquire语义,如果是读取-写入就是acquire-release语义;也是原子变量的默认语义。
所以,我们只需要将x和y的类型改为atmioc_int,就可以避免m和n同时为0的结果出现了。修改后的代码如下:
[code]#include #include #include using namespace std;atomic_int x(0);atomic_int y(0);int m = 0, n = 0;int main(){        while (1) {                x = y = 0;                thread t1([&]() { x = 1; m = y; });                thread t2([&]() { y = 1; n = x; });                t1.join(); t2.join();                if (m == 0 && n == 0) {                        cout
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表