This page on Thread Safety by Microsoft says shared_ptr
should be used even if there are multiple copies sharing the same object.
So does this mean that both of the following are acceptable? I've tried both and they appear to work fine.
EDIT: The actual business objective is to get string updates from the long running thread to the main thread. I figured I should use shared_ptr
since string
is not thread safe. Don't care about ownership honestly.
Option 1 (Passing reference):
auto status = std::make_shared<std::string>();
auto f = [&status]() {
...
*status = "current status";
...
};
std::thread t{f};
while(true) {
std::cout << *status << std::endl;
std::this_thread::sleep_for(1000ms);
if (*status == "completed") break;
}
t.join();
Option 2 (Making a copy):
auto status = std::make_shared<std::string>();
auto f = [](std::shared_ptr<std::string> s) {
...
*s= "current status";
...
};
std::thread t{f, status};
while(true) {
std::cout << *status << std::endl;
std::this_thread::sleep_for(1000ms);
if (*status == "completed") break;
}
t.join();
EDIT2: So apparently both these approaches are wrong for what I'm trying to achieve. I need to use std::mutex
(cppreference) and not muck around with shared_ptr
. See second half of this answer.
A std::shared_ptr consists of a control block and its resource. Yes, the control block is thread-safe; but no, the access to the resource is not thread-safe. That means, modifying the reference counter is an atomic operation and you have the guarantee that the resource will be deleted exactly once.
The shared reference counter counts the number of owners. Copying a std::shared_ptr increases the reference count by one. Destroying a std::shared_ptr decreases the reference count by one. If the reference count becomes zero, the resource will automatically be released.
In controlled circumstances you can pass the shared pointer by constant reference. Be sure that nobody is concurrently deleting the object, though this shouldn't be too hard if you're careful about to whom you give references. In general, you should pass the shared pointer as a straight copy.
So, we should use shared_ptr when we want to assign one raw pointer to multiple owners. // referring to the same managed object. When to use shared_ptr? Use shared_ptr if you want to share ownership of a resource.
(since C++11) std::shared_ptr is a smart pointer that retains shared ownership of an object through a pointer. Several shared_ptr objects may own the same object. The object is destroyed and its memory deallocated when either of the following happens:
In controlled circumstances you can pass the shared pointer by constant reference. Be sure that nobody is concurrently deleting the object, though this shouldn't be too hard if you're careful about to whom you give references. In general, you should pass the shared pointer as a straight copy.
The habit of passing shared_ptr by ref should not be followed in lambdas. If it gets destroyed elsewhere (passing by ref doesn't bump the ref count), your callback/lambda may crash. OTOH, passing it by value in lambdas too is dangerous and can cause memory leaks. Instead, we should pass weak_ptrto a shared_ptr.
Then it needs its own copy of the shared_ptr. So pass it by value. If a function simply needs to access an object owned by the caller, go ahead and pass by (const) reference, to avoid the overhead of copying the shared_ptr. The best practice in C++ is always to have clearly defined ownership semantics for your objects.
Typically, threads may outlive the scope where they are created. In such case, any local variable captured by reference may be destroyed while the thread is still running. If this is the case, then you should not capture by reference.
Furthermore, modifying a shared pointer object in one thread and accessing in another without synchronisation results in undefined behaviour. If that is what you're doing, then you should access the pointer using std::atomic_load
/atomic_store
functions, or simply copy the pointer into each thread. Note that you can capture by copy:
auto f = [status]() {
Furthermore, the shared pointer provides no extra thread safety to accessing the pointed object beyond keeping the ownership alive and ensuring it gets deleted exactly once. If the pointed type is not atomic, then modifying it in one thread and accessing in another without synchronisation results in undefined behaviour. If that is what you're doing, you need to use mutexes or something similar. Or copy the pointed object itself into each thread.
Regarding the edited question: Your examples apply to this last case. Both of them have undefined behaviour. You need synchronisation.
It is weird to accept shared_ptr
by reference as you lose the whole point of using shared_ptr
in the first place. You may just use a raw pointer instead.
There are cases when accepting by reference of shared_ptr
is legitimate but if you give a reference of it to a thread then it will cause UB once that instance of the shared_ptr
is destroyed and the thread still uses the shared_ptr
.
Primary purpose of shared_ptr
is to manage lifetime of the object. If you pass a reference of it to a thread then you throw away the whole purpose and advantages of the shared_ptr
.
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