简体   繁体   中英

Is this Singleton Implementation Thread-Safe?

I've read the question and answers here , and learned that a true thread-safe singleton can be implemented with correctly placed memory barrier, which is stated here in chapter 6. I've come up with my own version of singleton implementation, by reviewing the code I've seen no memory ordering problem so far, but it differs a lot from a typical DCLP approach. Do you think this is a reliable implementation?

static std::atomic<T*> _instance;
...
Singleton* get_instance() {
    ins = _instance.load(std::memory_order_acquire);
    if (ins == nullptr) {
        T* temp = nullptr;
        {            
            std::lock_guard<std::mutex> guard(_lock);
            temp = new(std::nothrow) T;
        }

        if(!_instance.compare_exchange_strong(nullptr, temp, std::memory_order_release)) {
            delete temp;
        }
    }
    return _instance.load(std::memory_order_acquire);
}

It is hard to beat a simple

class Singleton { ... };

Singleton* get_instance()
{
    static Singleton instance; // guaranteed to be thread safe in C++11
    return &instance;
}

Any access to the pointer after it has been returned is still not thread safe, but neither is access in your implementation.

Do you think this is a reliable implementation?

That depends on class T . Some classes must be singleton because there can be only one instance at any given moment, physically. Ie an attempt to create a second instance would certainly fail, for example due to both of them requiring exclusive access to some unique external resource.

Proposed implementation does allow for concurrent construction of two instances of T , which may lead to wonderful race-induced bugs.

Edit: Even with the lock protecting new T , this solution still does not guarantee that T is constructed only once.

Your implementation should only be used as an educational tool (use static in real software).
It has issues; multiple objects can be created (and all but one are deleted again), but at least it is thread-safe.

The first load can even be mo_relaxed since synchronization with compare_exchange(release) is covered by the load(mo_acquire) return statement.

In a DCLP implemention you would use a mutex to avoid creation of multiple objects and to block threads that are waiting for the creator to finish. Since your implementation already allows multiple objects, the mutex is not necessary from a correctness point of view (unless indeed the constructor is not thread safe).

If multiple threads create object T , the one that stores the pointer to _instance , releases the object (say thread N), while the others (say N+1) have to perform a load again; this second load(mo_acquire) in N+1 can never return nullptr . The compare_exchange in thread N+1 failed because it came later in the modification order than the first execution in thread N, which means that the compare_exchange (in N) also precedes the second load (in N+1) in the modification order and therefore it is guaranteed to return the object pointer to a synchronized object

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