我认为可以用atomic\u thread\u fence解决这个问题
atomic_thread_fence
仅当使用的内存排序弱于默认值时才有用
seq_cst
(参见
Jeff Preshing's article about C++11 fences
有关围栏和内存排序的更多信息。杰夫·普雷辛的文章很好;当然,在你尝试进行无锁编程时,一定要阅读其中的大部分内容)。
原子线围栏
只能限制当前线程的内存操作如何全局可见的重新排序。它本身并不等待其他线程中的某些内容。
当您尝试添加引用时,请准备好发现它已降至零
. 即
AddRef()
如果您来得太晚,并且另一个线程已经开始销毁refcounted对象,则可能会失败。
因此,AddRef的实现将执行以下操作
bool AddRef() {
int old_count = m_refcount;
do {
if (old_count <= 0) {
// we were too late; refcount had already dropped to zero
// so another thread is already destroying the data block
return false;
}
}while( !m_refcount.compare_exchange_weak(old_count, old_count+1) );
return true;
}
我们使用CAS循环作为条件
fetch_add
而不是
fetch\u添加
然后
联合国
如果旧值太低,则执行此操作。如果出现以下情况,则后者将需要额外的工作来避免比赛条件
二
线程立即递增。(第二个线程将看到和old\u count为1,并认为这是可以的。)你可以通过
Release
函数将refcount设置为一个大的负数
之前
开始销毁一个块,但这很容易验证,而且几乎总是在第一次尝试中成功的CAS几乎不会比实际的
fetch\u添加
. 与CAS相比,单独的原子负载几乎是免费的,尤其是在x86上。(您可以使用
memory_order_relaxed
使其在弱有序体系结构上几乎免费。)
请注意,refcount不能是
delete
当参考计数为零时
. 如果你这样做了,一个线程
get_datablock
并且做到了
m_datablock.load()
,然后休眠,然后用
datablock->AddRef()
如果指向的内存在休眠时被另一个线程删除,则可能会出现segfault(或导致其他未定义的行为)。
这个答案并不能解决问题
整体
(在仍然允许
exchange
在您的
set_datablock
应用程序编程接口。我不确定API设计是否真的有效。
这也不是一个完整的工作
atomic_shared_pointer
实施
如果你想知道它是如何工作的,看看它的文档,或者希望有人写了一篇关于它是如何实现的帖子。它的开源库实现已经存在,但可能很难阅读。