有人知道完全线程安全的shared_ptr
实现吗?例如,boost实现shared_ptr
对于目标(引用计数)是线程安全的,并且对于同时shared_ptr
实例读取也是安全的,但不是写入或读/写.
(参见Boost docs,例3,4和5).
是否存在对shared_ptr
实例完全线程安全的shared_ptr实现?
提升文档的人说:
shared_ptr对象提供与内置类型相同的线程安全级别.
但是如果你比较一个普通的指针(内置类型)smart_ptr
,那么同时写一个普通的指针是线程安全的,但同时写入a smart_ptr
不是.
编辑:我的意思是x86架构上的无锁实现.
EDIT2:这种智能指针的一个示例用例是有许多工作线程,它们使用当前工作项更新全局shared_ptr,并使用监视器线程获取工作项的随机样本.shared-ptr将拥有该工作项,直到为其分配了另一个工作项指针(从而销毁了以前的工作项).监视器将获得工作项的所有权(从而防止工作项被销毁),方法是将其分配给自己的shared-ptr.它可以通过XCHG和手动删除来完成,但如果共享ptr可以做到这一点会很好.
另一个例子是全局shared-ptr拥有"处理器",由某个线程分配,并由其他一些线程使用.当"用户"线程看到处理器shard-ptr为NULL时,它使用一些替代逻辑来进行处理.如果它不是NULL,它会通过将处理器分配给它自己的shared-ptr来防止处理器被破坏.
为这种完全线程安全的shared_ptr实现添加必要的障碍可能会影响性能.考虑下面的比赛(注意:伪代码比比皆是):
线程1:global_ptr = A;
线程2:global_ptr = B;
线程3:local_ptr = global_ptr;
如果我们将其分解为其组成操作:
线程1:
A.refcnt++; tmp_ptr = exchange(global_ptr, A); if (!--tmp_ptr.refcnt) delete tmp_ptr;
线程2:
B.refcnt++; tmp_ptr = exchange(global_ptr, B); if (!--tmp_ptr.refcnt) delete tmp_ptr;
线程3:
local_ptr = global_ptr; local_ptr.refcnt++;
显然,如果线程3在A的交换之后读取指针,那么B会在引用计数递增之前删除它,会发生不好的事情.
为了解决这个问题,我们需要在线程3进行refcnt更新时使用虚拟值:(注意:compare_exchange(variable,expected,new)原子地将变量中的值替换为new,如果它当前等于new,则返回true如果成功的话)
线程1:
A.refcnt++; tmp_ptr = global_ptr; while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, A)) tmp_ptr = global_ptr; if (!--tmp_ptr.refcnt) delete tmp_ptr;
线程2:
B.refcnt++; while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, A)) tmp_ptr = global_ptr; if (!--tmp_ptr.refcnt) delete tmp_ptr;
线程3:
tmp_ptr = global_ptr; while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, BAD_PTR)) tmp_ptr = global_ptr; local_ptr = tmp_ptr; local_ptr.refcnt++; global_ptr = tmp_ptr;
你现在必须在/ read/operation中间添加一个循环,其中包含atomics.这不是一件好事 - 在一些CPU上它可能非常昂贵.更重要的是,你也在忙着等待.你可以开始聪明地使用futexes等等 - 但是到那时你已经重新发明了锁.
这个成本必须由每个操作承担,并且在本质上与锁给你的方式非常相似,这就是为什么你通常看不到这样的线程安全的shared_ptr实现.如果您需要这样的东西,我建议将mutex和shared_ptr包装到一个便捷类中以自动锁定.