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
}
Regarding the code above, I know different threads reading and writing the same shared_ptr leads to race conditions. But how about weak_ptr? Is there any race condition in the code below? (My platform is 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)
}
std::shared_ptr and std::weak_ptr are already thread-safe.
The Thread Safety section of the Boost shared_ptr documentation says "shared_ptr objects offer the same level of thread safety as built-in types." The implementation must ensure that concurrent updates to separate shared_ptr instances are correct even when those instances share a reference count e.g.
std::weak_ptr is a smart pointer that holds a non-owning ("weak") reference to an object that is managed by std::shared_ptr. It must be converted to std::shared_ptr in order to access the referenced object.
By using a weak_ptr , you can create a shared_ptr that joins to an existing set of related instances, but only if the underlying memory resource is still valid. A weak_ptr itself does not participate in the reference counting, and therefore, it cannot prevent the reference count from going to zero.
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.
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:
const member function calls on the same instance are thread-safe. All observer functions are const. 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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With