About thread-safety of weak_ptr

对着背影说爱祢 提交于 2019-11-27 14:23:07
Christian Aichinger

I know I'm late, but this comes up when searching for "weak_ptr thread", and Casey's answer just isn't the whole truth. Both shared_ptr and weak_ptr can be used from threads without further synchronization.

For shared_ptr, there's a lot of documentation (e.g. on cppreference.com or on stackoverflow). You can safely access shared_ptr's that point to the same object from different threads. You just can't bang on the same pointer from two threads. In other words:

// 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;

To solve that problem in your code, pass g_s as parameter (by value)* to f1().

For weak pointers, the safety guarantee is hidden in the documentation for weak_ptr::lock:

Effectively returns expired() ? shared_ptr<T>() : shared_ptr<T>(*this), executed atomically.

You can use weak_ptr::lock() to get a shared_ptr from other threads without further synchronization. This is also confirmed here for Boost and in this SO answer by Chris Jester-Young.

Again, you have to make sure not to modify the same weak_ptr from one thread while accessing it from another, so pass g_w into f3() by value as well.

shared_ptr and weak_ptr fall under the same blanket threadsafety requirements as all other standard library types: simultaneous calls to member functions must be threadsafe if those member functions are non-modifying (const) (Detailed in C++11 §17.6.5.9 Data Race Avoidance [res.data.races]). Assignment operators are notably not const.

For brevity in the following discussion, different weak_ptrs and shared_ptrs that all are generated from the same original shared_ptr or unique_ptr will be termed 'instances'. weak_ptrs and shared_ptrs that don't share the same object do not need to be considered in this analysis. The general rules for assessing thread safety are:

  1. Simultaneous const member function calls on the same instance are thread-safe. All observer functions are const.
  2. Simultaneous calls on different instances are thread-safe, even when one of the calls is a modifier.
  3. Simultaneous calls on the same instance when at least one of the calls is a modifier are not thread-safe.

The following table shows thread-safety when two threads are operating on the same instance at the same time.

+---------------+----------+-------------------------+------------------------+
|   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                   |
+---------------+----------+-------------------------+------------------------+

The cppreference discussion of std::atomic(std::weak_ptr) is clearest on the safety of simultaneous accesses to different instances:

Note that the control block used by std::weak_ptr and std::shared_ptr is thread-safe: different non-atomic std::weak_ptr objects can be accessed using mutable operations, such as operator= or reset, simultaneously by multiple threads, even when these instances are copies or otherwise share the same control block internally.

C++20 introduces a std::atomic specialization of weak pointer that provides thread-safe modification of the same instance through appropriate synchronization. Note that when it comes to constructors, initialization from another instance is not atomic. For example, atomic<weak_ptr<T>> myptr(anotherWeakPtr); is not an atomic operation.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!