The shared_ptr
class itself is thread-safe, but this doesn't automatically make your application thread-safe.
The reference count on the shared_ptr
is incremented and decremented atomically. This means that multiple threads can simultaneously create copies of a shared_ptr
, and the shared_ptr
will correctly keep track of how many references exist.
However, this doesn't protect the object that the shared_ptr
points to. If multiple threads access the pointed-to object, you still need to synchronize those accesses to avoid data races.
Consider this example:
#include <iostream>
#include <memory>
#include <thread>
class Counter {
public:
void increment() {
++count;
}
int getCount() {
return count;
}
private:
int count = 0;
};
int main() {
auto counter = std::make_shared<Counter>();
std::thread t1([counter]() {
for (int i = 0; i < 1000000; ++i) {
counter->increment();
}
});
std::thread t2([counter]() {
for (int i = 0; i < 1000000; ++i) {
counter->increment();
}
});
t1.join();
t2.join();
std::cout << "Final count is " << counter->getCount() << '\n';
}
Final count is 1054186
In this example, two threads are sharing a Counter object through a shared_ptr
. However, the output of this program is undefined. Running the program a second time, our output is likely different:
Final count is 1060707
Even though the shared_ptr
is safely shared between the threads, the Counter object is not being accessed in a thread-safe way. The increment operation is not atomic, so the threads can interfere with each other, leading to lost increments.
To fix this, you would need to use a mutex or other synchronization primitive to protect the count variable:
#include <iostream>
#include <memory>
#include <mutex>
#include <thread>
class Counter {
public:
void increment() {
std::lock_guard<std::mutex> lock(mutex);
++count;
}
int getCount() {
std::lock_guard<std::mutex> lock(mutex);
return count;
}
private:
int count = 0;
std::mutex mutex;
};
int main() {/*...*/}
Final count is 2000000
Now, the access to count is protected by a mutex, so the program is thread-safe.
In summary, shared_ptr makes it safe to share ownership of an object between threads, but it doesn't automatically make the object itself thread-safe. That still requires proper synchronization in the object's member functions.
Answers to questions are automatically generated and may not have been reviewed.
std::shared_ptr
An introduction to shared memory ownership using std::shared_ptr