这是一个验证问题,以确保我能获得正确语言的律师欢迎的详细信息。
我想知道我可以在以下代码中使用std::shared_ptr
,而不需要使用atomic_shared_ptr
重写它。该示例已经简化,但实质上是example
的单个实例中的可能竞争条件(* 1)shared_ptr
的复制构造函数和(* 2)对{{1}的调用}。
请注意reset()
的普通指针在这里不起作用。如果p
在测试和p
调用之间变为空,则您将指向空指针。这是首先使用some_predicate
的原因。我想确保我实际上是在解决竞争条件,而不是简单地将其移到其他地方。
(这不是问题的关键所在,但乍一看这段代码可能看起来不对。shared_ptr
的行为是幂等的。一旦T
完成了工作,就会#&# 39;不再需要了。)
p
假设(* 1)和(* 2)同时执行。我可以想到比赛的两个可能的结果。 (在这两种情况下,代码都是正确的。)我的问题是这些是否只是 案例:
template< class T >
class example
{
shared_ptr< T > p ;
public:
example()
: p( make_shared( T() ) )
{}
void f()
{
shared_ptr< T > p_transient(p) ; // *1
if ( p_transient && p_transient -> some_predicate() )
{
p.reset() ; // *2
}
}
};
之前生效,因此reset
使p_transient
的成员实例保持活动状态。当T
返回时,删除程序在线程* 1中运行。 (f
的幂等性在这里发挥作用。)T
在副本生效之前生效,因此reset
初始化为空。删除器在p_transient
返回之前在线程* 2中运行。我不能动摇我在这里得到一些东西的感觉,所以我决定写下这个问题。我失踪了什么?
P.S。这就是我所缺少的。 reset
并不特别。不知何故,我认为这可能是因为我之前已经实现了智能指针(太多)。共享指针,特别是当指针也有弱指针时,几乎需要对其(隐藏)共享状态进行互斥保护。我认为保护必须涵盖整个对象,但它没有。
感谢响应者对标准的引用。数据竞争导致未定义行为的一般规则是1.10 / 27&#34;多线程执行和数据竞争[intro.multithread]&#34;。特别是,这意味着在这种情况下可能会违反后置条件。
答案 0 :(得分:2)
为了让#1和#2同时执行,那么你必须在两个不同的线程中调用example::f
。如果它们位于不同的example
个实例上,那么example::p
也会是不同的实例,因此没有问题。
如果它们位于相同的 example
实例上,那么您违反了有关标准库竞争条件的C ++一般规则。如果您访问不同的对象实例,您(通常)只能保证不受竞争条件的影响。因此,您可以push_back
在两个不同的线程中vector
,但不能相同 vector
。
shared_ptr
提供同样的保证。只要您不尝试从两个不同的线程访问相同的shared_ptr
实例,就可以了。一旦你这样做,所有的赌注都会被取消。
当你想从不同的线程原子地操纵同一个对象时,atomic shared_ptr
functions就是用来的。
例如:
shared_ptr< T > p_transient(atomic_load(&p));
if ( p_transient && p_transient -> some_predicate() )
{
atomic_store(&p, shared_ptr<T>());
}
或者,您可以将f
包装在互斥锁或某些内容中。这也意味着您不必使用shared_ptr
,因为您的可能破坏也包含在互斥锁中。
答案 1 :(得分:1)
您所看到的被称为数据竞赛。只要一个线程可以写入某些数据而另一个线程可以读取或写入该数据,就会被称为数据竞争。
数据竞赛是未定义的行为。这意味着可能发生的事情没有限制。我发誓博客条目Benign race cases: what could possibly go wrong?就是为了这些事情。他列出了一些可能出错的事情。
一个例子是,如果写入内存位置,实际上允许编译器使用此内存空间来保存溢出的寄存器。它不会经常发生,但它可能发生。上面提到的博客显示了一个极端的例子,这种形式的数据竞赛无意中发射了核导弹! (这里希望真正的核导弹发射计算机更加强大!)
如果要让两个线程与一段数据交互,则必须防止数据争用。这通常用互斥或原子来完成。