有人知道一个完全线程安全的shared_ptr实现吗?例如,shared_ptr的boost实现对于目标(重新计数)是线程安全的,对于同时读取shared_ptr实例也是安全的,但对于写或读/写是安全的。
(参见助推文档,示例3、4和5)。
是否有一个对shared_ptr实例完全线程安全的shared_ptr实现?
奇怪的是,助推医生说:
shared_ptr对象提供与内置类型相同的线程安全级别。
但是,如果将普通指针(内置类型)与smart_ptr进行比较,则普通指针的同时写入是线程安全的,但对smart_ptr的同步写入则不是。
编辑:我指的是x86体系结构上的无锁实现。
EDIT2:这种智能指针的一个示例用例是有许多工作线程,它们用当前的工作项更新全局shared_ptr,而监视器线程则随机获取工作项的样本。在将另一个工作项指针分配给它之前,shared将拥有该工作项(从而销毁以前的工作项)。通过将工作项分配给自己的共享ptr,监视器将获得工作项的所有权(从而防止工作项被销毁)。它可以通过XCHG和手动删除来完成,但是如果一个共享ptr能够做到的话,那就更好了。
另一个例子是全局共享ptr持有一个“处理器”,由某个线程分配,并由其他线程使用。当“用户”线程看到处理器shard-ptr为NULL时,它会使用其他逻辑进行处理。如果不为NULL,则通过将处理器分配给自己的shared-ptr来防止处理器被破坏。
发布于 2009-05-21 01:30:01
为这样一个完全线程安全的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(变量、预期、新的)原子化地将变量中的值替换为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/操作的中间有atomics。这不是一件好事-它可能是非常昂贵的一些CPU。更重要的是,你也很忙-也在等。你可以开始聪明地使用futexes和诸如此类的东西--但到了那个时候,你已经重新发明了锁。
这种成本必须由每个操作来承担,而且本质上与锁给您的东西非常相似,这就是为什么您通常看不到这样的线程安全的shared_ptr实现。如果您需要这样的东西,我建议将互斥锁和shared_ptr封装到一个方便的类中,以便自动锁定。
发布于 2009-01-14 03:58:13
同时写入内置指针肯定不是线程安全的。如果您真的想让自己发疯(例如,您可以有两个线程认为同一个指针有不同的值),那么考虑一下将相同值写入相同值的含义。
评论--内置没有双重删除的原因是因为它们根本没有删除(而且我使用的boost::shared_ptr的实现不会加倍删除,因为它使用了一个特殊的原子增量和递减,所以它只会进行一个删除,但是结果会有来自一个的指针和另一个的引用计数。或者两者的结合。这会很糟糕。)boost文档中的语句是正确的,您得到的保证与内置的保证是一样的。
RE: EDIT2 -您描述的第一种情况在使用内置和shared_ptrs之间有很大的不同。在一个(XCHG和手动删除)中,没有引用计数;当您这样做时,假设您是唯一的所有者。如果使用共享指针,则表示其他线程可能具有所有权,这使得事情变得更加复杂。我相信这是可能的比较和交换,但这将是非常不可移植的。
C++0x正在推出一个atomics库,这将使编写通用多线程代码变得更加容易。您可能不得不等到出现这种情况时,才能看到线程安全智能指针的良好跨平台引用实现。
发布于 2009-01-14 06:57:53
我不知道这样一个智能指针实现,但我不得不问:这种行为怎么可能有用呢?我能想到的唯一情况是,在哪些情况下可以同时找到指针更新:争用条件(即bug)。
这不是批评--可能有一个合法的用例,我只是想不起来。请让我知道!
Re: EDIT2,谢谢你提供了几个场景。听起来,原子指针写入在这些情况下是有用的。(一件小事:在第二个例子中,当您编写“如果它不是空的话,它通过将它分配给它自己的shared -ptr来防止处理器被破坏”时,我希望您的意思是首先将全局共享指针分配给本地共享指针,然后检查本地共享指针是否为空--这是您描述的那样,在测试全局共享指针之后,并且在将它分配给本地共享指针之前,它很容易成为空。)
https://stackoverflow.com/questions/441814
复制相似问题