Search code examples
c++multithreadingshared-ptr

When should I use a mutex with std::shared_ptr in C++ multithreading?


std::shared_ptr<Dog> pd;

void F() {
    pd = std::make_shared<Dog>("Smokey");
}

int main() {
    std::thread t1(F);
    std::thread t2(F);
    t1.join();
    t2.join();
    return 0;
}
std::shared_ptr<Dog> pd(new Dog("Gunner"));

void F() {
    std::shared_ptr<Dog> localCopy = pd;
}

int main() {
    std::thread t1(F);
    std::thread t2(F);
    t1.join();
    t2.join();
    return 0;
}

In C++, I understand that std::shared_ptr is thread-safe for reading and copying. But I'm a little confused about when I need to use a mutex to synchronize threads. I have two code snippets. In the first one, std::shared_ptr is being modified by multiple threads. In the second one, each thread is only reading from and copying the shared pointer. Do I need a mutex in both situations, or just in the first one? Why or why not?"


Solution

  • It's easier to understand this if you carefully identify all the moving pieces involved:

    • a reference-counted object, this is an object, somewhere, this is your Dog

    • the reference counter itself, this keeps track of the number of references to the reference-counted object

    • the shared pointer itself, that uses the reference counter

    These are all, discrete entities, that need to be considered, and evaluated, separately.

    As a rule of thumb, in C++ if an object is accessed from multiple execution threads, and at least one execution thread "modifies" it, in some way, then the execution threads must be "synchronized" with respect to this object; unless the object is "thread-safe". What does "synchronize" mean? Well, it means more than just a mutex, somewhere; but for this practical example this is what it means: you need to access the object while holding a lock on some mutex, somewhere.

    Data point: the reference counter is thread-safe. The smart pointer, a.k.a std::shared_ptr is not.

    pd = std::make_shared<Dog>("Smokey");
    

    This modifies the shared pointer, pd, in multiple execution threads. This requires synchronization, this is not thread safe. You need a mutex.

    std::shared_ptr<Dog> localCopy = pd;
    

    This makes a copy of pd, it does not modify it. This also juggles the reference counter, as part of making a copy (and destroying) it. The reference-counter is thread safe. The shared pointer is not being modified, only accessed. This is thread safe.