《白话C++》第12章并发,Page562 12.5.1 并发冲突分析

12.5.1并发冲突分析:

多个线程中,同时使用std::cout这个资源,就会触发资源并发访问冲突。cout代表标准输出(通常是指显示屏),多个线程同时输出,就容易造成屏幕上一片混乱。

运行结果:

例中每个并发都会执行10次这个操作:

cout << "i ==> " << i << " <~~ " << endl;

这看是一行代码,实际对应的CPU操作恐怕有上百个,至少我们语义层面划分,就可以分成四个:

① cout << "i ==> "

② cout  << i

③ cout << " <~~ "

④ cout << endl

CPU会在同时运行的线程间轮换,没有一个线程可以独占CPU时间而从容地完成10次循环;甚至很难独占CPU时间而一口气完成四个步骤。很可能一个线程输出"i ==> ",正要执行第二个步骤时,另一个线程就抢了CPU然后输出" <~~ "。

语言越高级(越方便人类表达思维),它的一行语句生成的机器指令往往越多。看个实例,C++代码如下

int main()
{
    int a = 1;
    int b = 2;
    int c;
    c = a + b;

    cout << c << endl;
}

主函数中前四行代码的汇编结果(为方便比较,汇编之前插入对应的源代码)是:

          int a = 1;
movl    $0x1, -0xc(%ebp)
          int b = 2;
movl    $0x2, -0x10(%ebp)
          int c;
          c = a + b;
mov     -0xc(%ebp), %edx
mov     -0x10(%ebp), %eax
add     %edx, %eax
mov     %eax, -0x14(%ebp)

我们可以看到 c = a + b;被编译成四行汇编语句。

某个操作有可能被打断,我们称该操作是“非原子操作”,不能或不允许被打断的操作,称为“原子操作”。

绝大多数C++写出的语句,都不是原子操作。

比如例中出现的原子操作,我们强调是“采用字面常量初始化”的代码,如果是使用旧的变量初始化新的变量(类似拷贝构造),那就变成非原子操作了:

        int d = c;
mov     -0x14(%ebp), %eax
mov     %eax, -0x18(%ebp)

同样存在一个从源内存(变量c)复制到寄存器,在从寄存器复制到目标内存(变量d)的过程。

不仅多数C++语句操作不是原子操作,甚至连单个汇编指令也大有可能不是原子操作,因为一个指令可能需要CPU花费多个时间周期执行。

那么,能在一个时间周期内完成的指令是不是就一定是原子操作?答案是否定的,到底什么操纵是不可分割的原子操作?

对于初学者来说,干脆认定所有操作都可能被别的线程打断。

比如:

COUNT = 5; //COUNT是一个其他线程也可以修改的变量
double a = (1 / COUNT);

两行语句执行之后,a的值是多少?单线程环境下是0.2,多线程环境下有可能是其他值,不幸遇上COUNT为0,程序还会挂掉。

如果心里没有对并发下的共用资源时时刻刻留神,我们就非常容易认定002行一定会紧接着001行执行,忘了会有其他线程可能正好也在运行,并且正好也修改了COUNT的值,并且修改的时机正好位于001和002行之间。

这就涉及到并发冲突问题难搞的第二个原因:冲突并不总发生,许多时候它们隐藏的很深。

#include <iostream>
#include <future>
#include <thread>

using namespace std;

int COUNT;

void foo_1()
{
    COUNT = 0;
}

int foo_2(int v)
{
    COUNT = 5;
    return v/COUNT;
}

int main()
{
    std::thread tr(foo_1);
    std::future <int> f = async(foo_2, 100);

    tr.join();
    cout << f.get() << endl;

    return 0;
}

这段代码运行100万次,可能也不会撞上冲突,但它的冲突逻辑问题客观存在。

我们在代码中,加几行输出代码:

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值