In the last article on smart pointers, we looked at
provides a simple and safe smart pointer to wrap heap allocations. As the name
implies, this smart pointer type cannot be shared between multiple threads.
So then how can you ensure that the memory is freed once all referring threads have finished with the resource? This is especially difficult when the thread lifecycle is non-deterministic.
The solution to sharing heap-allocated memory between threads is the
std::shared_ptr. While it does not address race conditions (see mutexes et
al), it does solve the problem of managing the lifecycle of a shared resource
between multiple threads.
shared_ptr implementation uses reference counting to keep track of how
many references to the object exist. Each time the smart pointer is replicated,
the reference count is increased. Each time a smart pointer goes out of scope,
the reference count is decreased. Once the final reference is gone (ie. the
reference count reaches zero), the object is deleted (and the destructor
Imagine we have a worker thread function which needs to use a shared resource. We can pass the worker thread a shared pointer:
We can use the object in as many threads as we need, and they can all terminate at an arbitrary time. The object will stay alive until the last thread has finished, and releases its reference.
In this example, the
main() function creates an instance, then passes the
smart pointer to several worker threads:
Note the use of
std::bind to pass the
obj pointer as a reference parameter
to the thread function; normally thread function parameters are passed by
The output will resemble something like:
+++ main() +++ Foo::Foo() main: spawning, obj use_count 1 main: joining, obj use_count 3 worker: running, obj use_count 3 worker: running, obj use_count 2 main: leaving, obj use_count 1 --- main() --- Foo::~Foo()
We clearly see the lifecycle of the
Foo instance. In
main, it is created
and thus the
use_count is 1 (before the threads are spawned). Then two
threads are spawned, increasing the
use_count to 3, which remains while the
threads are running. Main joins (or waits) to block on each thread terminating,
main continues, both threads have completed, thus the
back down to 1. Finally, the smart pointer goes out of scope at the end of main,
and is destroyed after main completes.
std::shared_ptr support makes it easy to share heap-allocated memory
between threads, or throughout code that has complex lifetime requirements.