C++ 关于弱线程ptr的线程安全性
C++ 关于弱线程ptr的线程安全性,c++,multithreading,c++11,weak-ptr,C++,Multithreading,C++11,Weak Ptr,shared_ptr和weak_ptr与所有其他标准库类型属于相同的总体线程安全要求:如果成员函数是非修改的(const)(详见C++11§17.6.5.9数据争用避免[res.Data.races]),则对成员函数的同时调用必须是线程安全的。赋值运算符显然不是常量,我知道我迟到了,但这是在搜索“弱线程”时出现的,Casey的答案并不完全正确无需进一步同步,即可从线程使用shared_ptr和weak_ptr。 对于共享\u ptr,有很多文档(例如on或on)。您可以从不同线程安全地访问指向同
shared_ptr
和weak_ptr
与所有其他标准库类型属于相同的总体线程安全要求:如果成员函数是非修改的(const
)(详见C++11§17.6.5.9数据争用避免[res.Data.races]),则对成员函数的同时调用必须是线程安全的。赋值运算符显然不是常量,我知道我迟到了,但这是在搜索“弱线程”时出现的,Casey的答案并不完全正确无需进一步同步,即可从线程使用shared_ptr
和weak_ptr
。
对于共享\u ptr
,有很多文档(例如on或on)。您可以从不同线程安全地访问指向同一对象的共享\u ptr
。你就是不能从两个线程中敲打同一个指针。换言之:
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)
}
//从两个线程使用p和p_拷贝是可以的。
//从两个线程使用p或从两个线程使用p和p_ref是非法的。
标准::共享\u ptr:
有效地返回expired()?shared_ptr():shared_ptr(*this)
,以原子方式执行
您可以使用弱\u ptr::lock()
从其他线程获取共享的\u ptr
,而无需进一步同步。克里斯·杰斯特·杨也证实了这一点
同样,在从另一个线程访问时,必须确保不要从一个线程修改相同的weak\u ptr
,因此也要通过值将g\u w
传递到f3()
。为了在下面的讨论中简洁起见,从同一原始共享ptr
或唯一ptr
生成的不同弱ptr
和共享ptr
将被称为“实例”<代码>弱\u ptr
s和不共享同一对象的共享\u ptr
s在本分析中不需要考虑。评估螺纹安全性的一般规则如下:
const
成员函数是线程安全的。所有观察者函数都是常量李>
// 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;
最清楚的是同时访问不同实例的安全性:
请注意,std::weak_ptr和std::shared_ptr使用的控制块是
线程安全:可以使用
可变操作,如运算符=或重置,同时由多个
线程,即使这些实例是副本或共享相同的实例
内部控制块
C++20引入了弱指针的
std::atomic
专门化,它通过适当的同步对同一实例进行线程安全的修改。注意,对于构造函数,来自另一个实例的初始化不是原子的。例如,atomicmyptr(另一个弱点)
不是一个原子操作。所以为了给我澄清这一点,我仍然不太确定如果对std::shared\u ptr调用reset(),同时对std:weak\u ptr调用lock()会发生什么
非常简单,如下所示:
+---------------+----------+-------------------------+------------------------+
| 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 |
+---------------+----------+-------------------------+------------------------+
std::shared_ptr sharedObject;
std::weak_ptr weakObject=共享对象;
void thread1()
{
std::shared_ptr leaseObject=weakObject.lock();//weakObject绑定到sharedObject
}
无效线程2()
{
reset();
}
我们假设sharedObject不与任何其他对象共享其指针
如果这两个命令(reset()和lock())同时执行,我希望:
- sharedObject已成功重置,weakObject.lock()返回null ptr,sharedObject将从内存中删除
- sharedObject已成功重置,weakObject.lock()返回指向sharedObject的指针。当leaseObject失去作用域时,sharedObject将从内存中删除
如果上面的代码未定义,我必须将我拥有的对象类中的std:mutex替换为类外的std:mutex,但这可能是另一个线程中的另一个问题。;) 是的,l_s3是否为空PTR完全是任意的。处理器的内核越多,f3()越有可能提前开始运行,因此为nullptr的可能性越小。这是一个标准的线程争用bug,与弱的ptr实现完全无关。实际上比这更糟糕:具有数据争用的程序具有未定义的行为。在典型的实现中,读取
f3
中的g_w
将看到值nullptr
或p_s
——但该标准不保证该行为。特别是,正常的单字写入是非原子的实现可能会返回一个“撕裂”值,即旧值和部分新值。一个弱指针可能是空的,如果你足够快去抓取它,那么对你来说是好的。如果不是,那就太糟糕了,但这不会在软件中引起错误。使用弱引用时,需要确保两条路径(锁定成功和失败)都是有效路径。如果你在任何一种情况下都有一个bug,它与比赛条件无关,它只是一个缺陷。如果您需要随时锁定,请将其共享。f3()
和f4()
的可见代码很好。但是,注释后的代码不是很好://1。p_s destroy将修改g_w(写入g_w)
。在f4()
中更新g_w
,同时通过g_w.lock()
在f3()
中读取g_w
是未定义的行为!std::shared\u ptr上有更强的线程安全保证
+---------------+----------+-------------------------+------------------------+
| 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 |
+---------------+----------+-------------------------+------------------------+
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();
}