C++ 20 原子引用 (一)

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

C++ 20 原子引用 一

std::atomic_ref{}

std::atomic_ref类型对其引用的对象进行原子操作。

使用std::atomic_ref 进行多线程读写时不会造成数据争用。被引用对象的生命周期必须超过std::atomic_ref 。操作std::atomic_ref 的子对象是未定义行为。

错误示例

你可能认为在一个原子内使用引用可以实现这种操作实际上不可以

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

struct ExpensiveToCopy
{
	int counter{};
};

int getRandom(int begin, int end)
{

	std::random_device seed; 
	std::mt19937 engine(seed()); 
	std::uniform_int_distribution<> uniformDist(begin, end);
	return uniformDist(engine);
}

void count(ExpensiveToCopy& exp)
{

	std::vector<std::thread> v;
	std::atomic<int> counter{exp.counter}; 
	for (int n = 0; n < 10; ++n)
	{

		v.emplace_back([&counter]
		{
			auto randomNumber = getRandom(100, 200); 
			for (int i = 0; i < randomNumber; ++i) { ++counter; }
		});
	}
	for (auto& t : v) t.join();
}

int main()
{
	std::cout << std::endl;
	ExpensiveToCopy exp; 
	count(exp);
	std::cout << "exp.counter: " << exp.counter << '\n';
	std::cout << std::endl;
}

image.png
最后的结果应该接近1500所以出现了错误原因是 std::atomic<int> counter{exp.counter}实际上创建了一个副本。

接下来使用一个简单的示例演示一下

int main(int argc, char* argv[])
{
	int val{ 10086 };
	int& ref = val;
	std::atomic<int> atomicRef{ ref };
	++atomicRef;
	std::cout << std::format("val = {}, ref = {}, atomicRef = {}", val, ref, atomicRef.load()) << std::endl;
}

image.png

使用std::atomic_ref<int> counter{exp.counter}代替std::atomic<int> counter{exp.counter}解决这个问题

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

struct ExpensiveToCopy
{
	int counter{};
};

int getRandom(int begin, int end)
{
	std::random_device seed;
	std::mt19937 engine(seed());
	std::uniform_int_distribution<> uniformDist(begin, end);
	return uniformDist(engine);
}

void count(ExpensiveToCopy& exp)
{
	std::vector<std::thread> v;
	std::atomic_ref<int> counter{exp.counter};
	for (int n = 0; n < 10; ++n)
	{
		v.emplace_back([&counter]
		{
			auto randomNumber = getRandom(100, 200);
			for (int i = 0; i < randomNumber; ++i) { ++counter; }
		});
	}
	for (auto& t : v) t.join();
}

int main()
{
	std::cout << std::endl;
	ExpensiveToCopy exp;
	count(exp);
	std::cout << "exp.counter: " << exp.counter << '\n';
	std::cout << std::endl;
}

看到这里你可能会想为什么不把计数器最开始就定义成原子变量呢像这样

struct ExpensiveToCopy {
std::atomic<int> counter{};
};

这确实是一个有效的方法但是对原子变量的操作时同步的使用std::atomic_ref<int> counter 可以让你显式的控制何时需要对原子变量进行原子访问因为大多数时间里你可能只需要读取这个变量。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: c++

“C++ 20 原子引用 (一)” 的相关文章