简体   繁体   中英

Is boost::atomic_shared_ptr absolutely safe?

It is well known that std::shared_ptr is not thread safe. So it is easy to find a lot of crashing code samples in the web with simple programs to illustrate disadvantages of std::shared_ptr. But I can't find any for the boost::atomic_shared_ptr Does this mean that I can use boost::atomic_shared_ptr without any fear it can crash my app in the multithreaded environment? (Of course the boost::atomic_shared_ptr can contain bugs. So I want to know - it is safe "by design" or not.)

You can use shared_pointer with atomic_load / atomic_store anyways ever since C++11, even though it is superseded in c++20:

https://en.cppreference.com/w/cpp/memory/shared_ptr/atomic

I can't find a lot of examples myself, though I used it here: How to use boost::atomic_store with shared_ptr<T> and shared_ptr<const T>? , and here https://github.com/sehe/msghub/blob/std-over-boost/src/msghub.cpp#L25

Regardless of all this, none of this is going to make your code safe, because you'll still be sharing the objects pointed to by ths shared pointer, which makes your code every bit as prone to Data Races as before.

You still need to add proper synchronization to your code.

std::atomicstd::shared_ptr<> is thread-safe but ONLY for reading and writing the value of the pointer itself. It's use does not make all uses of shared_ptr thread-safe.

Consider this:

// using a single writer of data pointed to by foo for simplicity.

// contract: only set values from main thread (for example)
//           set values, then call apply_changes when all your parameters
//           are correct.
struct Foo
{
    void set_valueA(int x);
    //...  
    void set_valueZ(int x);

    void apply_changes();   
};   

// using global data for silplicity, tis could be a member of some class,
// or in any stable memory location in an application.       

// More than one thread may reallocate or reset this object, hence the std::atomic. 
std::atomic<std::shared_ptr<Foo>> global_ptr;   

void some_function()
{
    // Set or reset  global_ptr attributes

    if (!global_ptr)
        return;

    global_ptr->set_valueA(42);    // a bad_alloc exception could 
                                   // happen here

    // what happens if global_ptr is set or reset
    // by another thread in between these two statements?

    global_ptr->set_valueB(17);    // a bad_alloc exception may occur here.
    global_ptr->apply_changes();   // undefined behavior as per contract of Foo 
                                   // may happen here.
}

// for this reason, proper usage would be...

void some_function()
{
    // Set or reset  global_ptr attributes

    // since global_ptr is atomic, it is guaranteed that this line will not crash
    // and will always return a well-formed shared_ptr. If fact, that's the
    // one and only guarantee given by std::atomic<std::shared_ptr<>>.
    if (std::shared_ptr<Foo> local_copy = global_ptr)
    {
        // Note that local_copy is guaranteed to point to the same object until 
        // end of scope. 

        local_copy->set_valueA(42);
        local_copy->set_valueB(17);
        local_copy->apply_changes();

        // if global_ptr has changed, then memory will be released when 
        //  exiting scope. 
    }
}

So, imho, some basic precautions must still be taken when using atomic shared pointers, either for read or write operations on the pointed to data.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM