C++11中的原子量和内存序详解

2020-01-06 19:27:01于海丽

实际上这些特化就是相当于取了一个别名,本质上是同样的定义。而对于整形的特化而言,会有一些特殊的成员函数,例如原子加fetch_add、原子减fetch_sub、原子与fetch_and、原子或fetch_or等。常见操作符++、--、+=、&= 等也有对应的重载版本。

接下来以int类型为例,解决我们的前面提到的i++ 场景中的问题。先定义一个int类型的原子量:


std::atomic<int> i;

由于int型的原子量重载了++ 操作符,所以i++ 是一个不可分割的原子操作,我们用多个线程执行i++ 操作来进行验证,测试


#include <iostream>
#include <atomic>
#include <vector>
#include <functional>
#include <thread>

std::atomic<int> i;
const int count = 100000;
const int n = 10;

void add()
{
 for (int j = 0; j < count; ++j)
 i++;
}

int main()
{
 i.store(0);
 std::vector<std::thread> workers;
 std::cout << "start " << n << " workers, "
  << "every woker inc " << count << " times" << std::endl;

 for (int j = 0; j < n; ++j)
 workers.push_back(std::move(std::thread(add)));

 for (auto & w : workers)
 w.join();

 std::cout << "workers end "
  << "finally i is " << i << std::endl;

 if (i == n * count)
 std::cout << "i++ test passed!" << std::endl;
 else
 std::cout << "i++ test failed!" << std::endl;

 return 0;
}

在测试中,我们定义了一个原子量i,在main函数开始的时候初始化为0,然后启动10个线程,每个线程执行i++操作十万次,最终检查i的值是否正确。执行的最后结果如下:


start 10 workers, every woker inc 100000 times
workers end finally i is 1000000
i++ test passed!

上面我们可以看到,10个线程同时进行大量的自增操作,i的值依然正常。假如我们把i修改为一个普通的int变量,再次执行程序可以得到结果如下:


start 10 workers, every woker inc 100000 times
workers end finally i is 445227
i++ test failed!