use*_*282 38 c++ lock-free unique-ptr atomic-swap c++11
交换两个unique_ptrs并不保证是线程安全的.
std::unique_ptr<T> a, b;
std::swap(a, b); // not threadsafe
Run Code Online (Sandbox Code Playgroud)
由于我需要原子指针交换,因为我喜欢所有权处理unique_ptr,是否有一种简单的方法将它们组合起来?
编辑:如果这是不可能的,我愿意接受替代方案.我至少想做这样的事情:
threadshared_unique_ptr<T> global;
void f() {
threadlocal_unique_ptr<T> local(new T(...));
local.swap_content(global); // atomically for global
}
Run Code Online (Sandbox Code Playgroud)
在C++ 11中这样做的惯用方法是什么?
Sta*_*tas 21
对于这个问题,似乎没有通用的无锁解决方案.为此,您需要有可能将新值原子地写入两个非连续的内存位置.这称为DCAS,但在英特尔处理器中不可用.
这是可能的,因为它只需要以原子方式保存新值global并接收其旧值.我的第一个想法是使用CAS操作.看看下面的代码,以获得一个想法:
std::atomic<T*> global;
void f() {
T* local = new T;
T* temp = nullptr;
do {
temp = global; // 1
} while(!std::atomic_compare_exchange_weak(&global, &temp, local)); // 2
delete temp;
}
Run Code Online (Sandbox Code Playgroud)
脚步
global指针templocal到globalif global仍然等于temp(它没有被其他线程更改).如果不是这样,请再试一次.实际上,CAS那里是矫枉过正,因为global在改变之前我们没有对旧值做任何特别的事情.所以,我们只能使用原子交换操作:
std::atomic<T*> global;
void f() {
T* local = new T;
T* temp = std::atomic_exchange(&global, local);
delete temp;
}
Run Code Online (Sandbox Code Playgroud)
请参阅Jonathan的答案,了解更简短,更优雅的解决方案.
无论如何,你必须编写自己的智能指针.你不能在标准中使用这个技巧unique_ptr.
Jon*_*ely 21
以原子方式修改两个变量的惯用方法是使用锁.
如果std::unique_ptr没有锁,你就无法做到.甚至std::atomic<int>没有提供以原子方式交换两个值的方法.您可以自动更新一个并返回其先前的值,但就std::atomicAPI 而言,交换在概念上是三个步骤:
auto tmp = a.load();
tmp = b.exchange(tmp);
a.store(tmp);
Run Code Online (Sandbox Code Playgroud)
这是一个原子读取,后跟原子读 - 修改 - 写,然后是原子写.每个步骤都可以原子方式完成,但是如果没有锁定,你就无法完成所有三个步骤.
对于不可复制的值,例如std::unique_ptr<T>你甚至不能使用上面的load和store操作,但必须这样做:
auto tmp = a.exchange(nullptr);
tmp = b.exchange(tmp);
a.exchange(tmp);
Run Code Online (Sandbox Code Playgroud)
这是三个读 - 修改 - 写操作.(你不能真正使用std::atomic<std::unique_ptr<T>>它,因为它需要一个简单的可复制的参数类型,并且std::unique_ptr<T>不是任何类型的可复制.)
要用更少的操作来完成它将需要一个不受支持的不同API,std::atomic因为它无法实现,因为正如Stas的回答所说,大多数处理器都无法实现.C++标准不习惯标准化所有现代架构都不可能实现的功能.(无论如何都不是故意的!)
编辑:您更新的问题询问了一个非常不同的问题,在第二个示例中,您不需要影响两个对象的原子交换.只global在线程之间共享,因此您不关心更新是否local是原子的,您只需要自动更新global并检索旧值.规范的C++ 11方法就是这样,std:atomic<T*>你甚至不需要第二个变量:
atomic<T*> global;
void f() {
delete global.exchange(new T(...));
}
Run Code Online (Sandbox Code Playgroud)
这是一个读 - 修改 - 写操作.