Multithreaded Programming with shared_ptr and Atomic References
Learn safe and efficient multithreaded programming using shared_ptr and atomic references with an example.
Using shared_ptr
in a multithreaded environment
What about the std::shared_ptr
? Can it be used in a multithreaded environment, and how is reference counting handled when multiple threads are accessing an object referenced by multiple shared pointers?
To understand shared pointers and thread safety, we need to recall how std::shared_ptr
is typically implemented. Consider the following code:
// Thread 1auto p1 = std::make_shared<int>(42);
The code creates an int
on the heap and a reference-counted smart pointer pointing at the int
object. When creating the shared pointer with std::make_shared()
, a control block is created next to the int
. The control block contains, among other things, a variable for the reference count, which is incremented whenever a new pointer to the int
is created and decremented whenever a pointer to the int
is destroyed. To summarize, when the preceding code line is executed, three separate entities are created:
- The actual
std::shared_ptr
objectp1
(local variable on the stack) - A control block (heap object)
- An
int
(heap object)
The following figure shows the three objects:
Now, consider what would happen if the following code was executed by a second thread:
// Thread 2auto p2 = p1;
We are creating a new pointer pointing at the int
(and the control block). When creating the p2
pointer, we read p1
, but we also need to mutate the control block when updating the reference counter. The control block lives on the heap and is shared among the two threads, so it needs synchronization to avoid data races. Since the control block is an implementation detail hidden behind ...