[英]About thread-safety of weak_ptr
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
呢? 下面的代码中是否有任何竞争条件? (我的平台是微软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)
}
我知道我迟到了,但是在搜索“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)
expired() ? shared_ptr<T>() : shared_ptr<T>(*this)
,以原子方式执行。
您可以使用weak_ptr::lock()
从其他线程获取shared_ptr
而无需进一步同步。 这在Boost 和 Chris Jester-Young 的SO 回答中也得到了证实。
同样,您必须确保在从另一个线程访问它时不要从一个线程修改相同的weak_ptr
,因此也将g_w
按值传递给f3()
。
shared_ptr
和weak_ptr
属于与所有其他标准库类型相同的全面线程安全要求:如果成员函数是不可修改的 ( const
),则对成员函数的同时调用必须是线程安全的(详见 C++11 §17.6.5.9 数据竞争避免[res.data.races])。 赋值运算符显然不是const
。
为了在下面的讨论中简洁起见,所有从相同的原始shared_ptr
或unique_ptr
生成的不同的weak_ptr
和shared_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 对 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);
不是原子操作。
所以为了让我清除它,我仍然不太确定如果在 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() )同时发生,我希望:
sharedObject 成功重置并且weakObject.lock() 返回nullptr,并且sharedObject 从内存中删除。
sharedObject 成功重置并且weakObject.lock() 返回一个指向sharedObject 的指针。 当leaseObject 失去作用域时,sharedObject 将从内存中删除。
如果上面的代码未定义,我必须将我拥有的 Object 类中的 std:mutex 替换到类之外,但这可能是另一个线程中的另一个问题。 ;)
跨线程使用weak_ptr和shared_ptr是安全的; weak_ptr/shared_ptr 对象本身不是线程安全的。 您不能跨线程读取/写入单个智能指针。
像您一样访问 g_s 并不安全,无论是 shared_ptr 还是weak_ptr。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.