是“std::shared_ptr”atomic与“reset()”的复制构造函数
Is the copy constructor of `std::shared_ptr` atomic versus `reset()`?
这是一个验证问题,以确保我得到了正确的细节—欢迎语言律师。
我想知道我可以在下面的代码中使用std::shared_ptr
,而不需要用atomic_shared_ptr
重写它。该示例已被简化,但本质是example
的单个实例中(*1(shared_ptr
的复制构造函数和(*2(对reset()
的调用之间可能存在竞争条件。
请注意,p
的纯指针在这里不起作用。如果p
在测试和对some_predicate
的调用之间变为null,那么您将间接使用null指针。这就是首先使用shared_ptr
的原因。我想确保我真的解决了比赛条件,而不是简单地把它转移到其他地方。
(这不是问题的重点,但乍一看,这段代码可能是错误的。T
的行为是幂等的。一旦p
完成了它的工作,就不再需要它了。(
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
}
}
};
假设(*1(和(*2(同时执行。我能想到比赛的两个可能结果。(这两种情况下的代码都是正确的。(我的问题是,这是否是仅的情况:
- 副本在
reset
之前生效,因此p_transient
使T
的成员实例保持活动状态。当f
返回时,deleter在线程*1中运行。(T
的幂等性在这里发挥作用。( reset
在复制之前生效,因此p_transient
初始化为空。在reset
返回之前,deleter在线程*2中运行
我无法摆脱我在这里不劳而获的感觉,所以我决定写下这个问题。我缺了什么吗?
附言:这是我错过的。shared_ptr
并不特别。不知怎么的,我认为会的,也许是因为我以前实现过(太多次(智能指针。共享指针,尤其是在存在弱指针的情况下,几乎需要对其(隐藏的(共享状态进行互斥保护。我认为保护一定涵盖了整个物体,但事实并非如此。
感谢响应者对该标准的参考。数据竞赛导致未定义行为的一般规则是1.10/27"多线程执行和数据竞赛[interr.multithreads]"。特别是,这意味着在这种情况下可能会违反后置条件。
为了使#1和#2同时执行,必须在两个不同的线程中调用example::f
。如果这些在不同的example
实例上,那么example::p
也将是不同的实例,所以没有问题。
如果这些位于同一example
实例上,则您违反了C++关于标准库竞争条件的一般规则。只有当您访问不同的对象实例时,才能(通常(保证您不受种族条件的影响。因此,您可以在两个不同线程中的两个不同的vector
上使用push_back
,但不能使用相同的vector
。
shared_ptr
提供同样的保证。只要您不试图从两个不同的线程访问同一个shared_ptr
实例,就可以了。一旦你这样做,所有的赌注都会被取消。
原子shared_ptr
函数适用于希望从不同线程以原子方式操作同一对象的情况。
例如:
shared_ptr< T > p_transient(atomic_load(&p));
if ( p_transient && p_transient -> some_predicate() )
{
atomic_store(&p, shared_ptr<T>());
}
或者,您可以将f
封装在互斥对象或类似对象中。这也意味着您不必使用shared_ptr
,因为您可能的破坏也包含在互斥体中。
您所看到的被称为数据竞赛。任何时候,一个线程可以写入某些数据,而另一个线程可能读取或写入该数据,这就是所谓的数据竞赛。
数据竞赛是未定义的行为。这意味着可能发生的事情没有限制。我对博客文章Benign种族案例发誓:可能出了什么问题?因为这些事情。他列出了一系列可能出错的事情。
一个例子是,如果向内存位置写入,编译器实际上可以使用这个内存空间来保存溢出的寄存器。这种情况并不经常发生,但它可以发生。上面提到的博客展示了一个极端的例子,这种形式的数据竞赛无意中发射了一枚核导弹!(希望真正的核导弹发射计算机更健壮!(
如果要让两个线程与一段数据交互,则必须防止数据争用。这通常是通过互斥或原子来完成的。
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 当从函数参数中的临时值调用复制构造函数时
- 如果有一个模板构造函数只有一个泛型参数,为什么我必须有一个复制构造函数
- 为什么需要复制构造函数,在哪些情况下它们非常有用
- 使用仅使用一次的变量调用的复制构造函数.这可能是通过调用move构造函数进行编译器优化的情况吗
- 为什么类中的ostringstream类型的成员会导致";调用隐含删除复制构造函数";错误
- 复制构造函数、赋值运算符C++
- std::ofstream 作为类成员删除复制构造函数?
- 复制构造函数C++无法正确复制指针
- 关于复制构造函数的一个棘手问题
- 为什么调用复制构造函数而不是移动构造函数?
- 填充上编译器生成的复制构造函数之间的不一致
- C++ 对象指针数组的复制构造函数
- C++ 基本 CTOR 说明 - 为什么不调用赋值/复制构造函数
- 防止在复制构造函数中隐式调用基构造函数
- 为用户定义的类正确调用复制构造函数/赋值运算符
- 具有已删除移动和复制构造函数的类的就地构造
- 复制构造函数隐式转换问题
- 复制构造函数中的递归调用