并发入门之memory_order理解

摘要

  多线程并发需要我们有一个代码执行粒度的考虑,比如一个简单的 int * pCount = new int(0);程序运行时的汇编语句可能有三句,先new内存空间,再调用int的构造,最后把地址返回pCount。这个顺序随编译器的优化可能会变,也就是先返回了地址,后进行的构造。
  当其他线程依据指针为非空判定为对象已经创建完成,而实际上对象的构造还没有被执行,这就为我们的程序埋下了重磅隐患。
  由此引出了一个需要重点学习的知识点,就是计算机cpu与内存相关的代码是按什么顺序执行的?因为CPU读写内存是跑的固定代码,所以这个执行顺序是编译器指定好的。也就是我们要站在编译器的角度进行思考为啥执行顺序是这样的!

直接看下面这个代码

// EvaluationOrder_Atomic.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <vector>
#include <iostream>
#include <thread>
#include <atomic>

int cnt_2 = 0;
void f()
{
    for (int n = 0; n < 1000; ++n) {
        cnt_2++;
    }
}

int main()
{
    std::vector<std::thread> v;
    for (int n = 0; n < 10; ++n) {
        v.emplace_back(f);
    }
    for (auto& t : v) {
        t.join();
    }
    std::cout << "Final  counter value cnt_2 is " << cnt_2 << '\n';
}

  启动十个线程,每个线程对一个全局变量进行++操作1000次,最后的执行结果应该是1000*10等于10000吧?然鹅实际的输出如下图9856。产生这个结果的原因是多线程下,++操作不是原子的,有道面试题是:

两个线程对全局变量i,跑i++100次,结果最大值是多少?最小值是多少

,搞懂这个++操作的理解就到位了。
在这里插入图片描述
  再看下图,我把++操作的汇编语言截图了,

  1. 第一步是先mov完成从内存到寄存器的工作
  2. 第二步是寄存器加1
  3. 第三步是寄存器回写内存

在这里插入图片描述

So,如何保证i++操作多线程下的原子性呢?

  答案是对内存加“临界区”,如下的汇编代码就是对加加操作的变量加了锁,别的线程访问这个内存会被挂起。这样就保证了在写这个内存的时候,其他线程不会读到脏数据(无效数据,为啥无效?因为读的时候正在写)。

//这个代码是++操作的线程安全版本!
		int* pcnt_2 = NULL;
		pcnt_2 = &cnt_2;
		__asm
		{
			mov      ecx, pcnt_2;
			mov      eax, 1;
			lock xadd[ecx], eax; //加
			inc      eax;
		}

  那么诸如此简单的指令我们可以通过汇编很容易实现,那复杂一些的怎么办?这就引出了本文的主角,C++新支持的特性原子操作<atomic>与内存序"memory_order"

正文

   紧接着上面汇编实现多线程++i操作的安全方案,这里给出c++保证多线程操作安全的方案。

typedef enum memory_order {
memory_order_relaxed, // 不对执行顺序做保证,只保证此操作是原子的
memory_order_acquire, // 本线程中,所有后续的读操作必须在本条原子操作完成后执行
memory_order_release, // 本线程中,所有之前的写操作完成后才能执行本条原子操作
memory_order_acq_rel, // 同时包含 memory_order_acquire 和 memory_order_release
memory_order_consume, // 本线程中,所有后续的有关本原子类型的操作,必须在本条原子操作完成之后执行
memory_order_seq_cst // 全部存取都按顺序执行
} memory_order;
c++ 保证++操作的原子性
std::atomic<int> cnt = { 0 };
void f()
{
	for (int n = 0; n < 1000; ++n) 
	{
		cnt.fetch_add(1, std::memory_order_relaxed);//保证操作的原子性,即相关的汇编指令不会被打断
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值