std::shared_ptr<int> g_s = std::make_shared<int>(1);
void f1()
{
std::shared_ptr<int>l_s1 = g_s; // read g_s
}
void f2()
{
std::shared_ptr<int> l_s2 = std::make_shared<int>(3);
std::thread th(f1);
th.detach();
g_s = l_s2; // write g_s
}
关于上面的代码,我知道读取和写入相同shared_ptr
的不同线程会导致竞争条件。但weak_ptr
怎么样?下面的代码中是否有竞争条件? (我的平台是Microsoft VS2013。)
std::weak_ptr<int> g_w;
void f3()
{
std::shared_ptr<int>l_s3 = g_w.lock(); //2. here will read g_w
if (l_s3)
{
;/.....
}
}
void f4()
{
std::shared_ptr<int> p_s = std::make_shared<int>(1);
g_w = p_s;
std::thread th(f3);
th.detach();
// 1. p_s destory will motify g_w (write g_w)
}
答案 0 :(得分:32)
我知道我迟到了,但是在搜索“weak_ptr线程”时会出现这种情况,而Casey的回答并不是全部真相。 可以在线程中使用shared_ptr
和weak_ptr
,而无需进一步同步。
对于shared_ptr
,有很多文档(例如在cppreference.com或stackoverflow上)。您可以安全地访问shared_ptr
,指向来自不同线程的同一对象。你无法从两个线程敲击相同的指针。换句话说:
// Using p and p_copy from two threads is fine.
// Using p from two threads or p and p_ref from two threads is illegal.
std::shared_ptr<A> p = std::make_shared<A>();
std::shared_ptr<A> &p_ref = p;
std::shared_ptr<A> p_copy = p;
要在代码中解决该问题,请将g_s
作为参数(按值)*传递给f1()
。
对于弱指针,安全保证隐藏在weak_ptr::lock的文档中:
有效地返回
expired() ? shared_ptr<T>() : shared_ptr<T>(*this)
,以原子方式执行。
您可以使用weak_ptr::lock()
从其他线程获取shared_ptr
而无需进一步同步。对于Boost和Chris Jester-Young在here中也确认了this SO answer。
同样,您必须确保在从另一个线程访问另一个线程时不要修改相同的weak_ptr
,因此也要将g_w
传递给f3()
。
答案 1 :(得分:4)
shared_ptr
和weak_ptr
属于与所有其他标准库类型相同的全局线程安全要求:如果这些成员函数未进行修改,则对成员函数的同时调用必须是线程安全的(const
)(详见C ++11§17.6.5.9数据竞争规避[res.data.races])。分配运算符特别是不 const
。
答案 2 :(得分:2)
为简洁起见,在下面的讨论中,全部由相同的原始weak_ptr
或shared_ptr
生成的不同shared_ptr
和unique_ptr
将被称为“实例”。在此分析中无需考虑不共享同一对象的weak_ptr
和shared_ptr
。评估线程安全性的一般规则是:
const
成员函数调用是线程安全的。所有观察者功能均为const
。 下表显示了两个线程同时在同一实例上运行时的线程安全性。
+---------------+----------+-------------------------+------------------------+
| operation | type | other thread modifying | other thread observing |
+---------------+----------+-------------------------+------------------------+
| (constructor) | | not applicable | not applicable |
| (destructor) | | unsafe | unsafe |
| operator= | modifier | unsafe | unsafe |
| reset | modifier | unsafe | unsafe |
| swap | modifier | unsafe | unsafe |
| use_count | observer | unsafe | safe |
| expired | observer | unsafe | safe |
| lock | observer | unsafe | safe |
| owner_before | observer | unsafe | safe |
+---------------+----------+-------------------------+------------------------+
cppreference discussion of std::atomic(std::weak_ptr)在同时访问不同实例的安全性方面最清楚:
请注意,std :: weak_ptr和std :: shared_ptr使用的控制块为 线程安全:可以使用以下方式访问不同的非原子std :: weak_ptr对象 多个操作同时进行可变操作,例如operator =或reset 线程,即使这些实例是副本或共享相同的实例 内部控制块。
C ++ 20引入了弱指针的std::atomic
特化,它通过适当的同步为同一实例提供线程安全的修改。注意,对于构造函数,从另一个实例进行初始化不是原子的。例如,atomic<weak_ptr<T>> myptr(anotherWeakPtr);
不是原子操作。
答案 3 :(得分:0)
所以要为我清除此信息,我仍然不确定如果在std :: shared_ptr上调用reset(),同时为std:weak_ptr调用lock()会发生什么。
极为简化:
std::shared_ptr<Object> sharedObject;
std::weak_ptr<Object> weakObject = sharedObject;
void thread1()
{
std::shared_ptr<Object> leaseObject = weakObject.lock(); //weakObject is bound to sharedObject
}
void thread2()
{
sharedObject.reset();
}
我们假定sharedObject不与其他任何对象共享其指针。
如果这两个命令(reset()和lock())完全同时发生,那么我希望两者之一:
共享对象已成功重置,weakObject.lock()返回nullptr,并且该共享对象已从内存中删除。
已成功重置sharedObject,并且weakObject.lock()返回指向sharedObject的指针。当leaseObject失去作用域时,将从内存中删除sharedObject。
如果上面的代码未定义,则必须将我拥有的Object类中的std:mutex替换为该类之外的对象,但这可能是另一个线程中的另一个问题。 ;)