2015-11-10 173 views
4

单线程实现线程安全吗? :: Instance方法应该是正确的,Dispose是我自己创建的,所以想确保我没有忽略任何东西。单线程实现线程安全吗?

std::atomic<S *> S::_instance; 
std::mutex S::_singleton_mutex; 

S& S::Instance() 
{ 
    using namespace std; 
    S * tmp = _instance.load(memory_order_relaxed); 
    atomic_thread_fence(memory_order_acquire); 
    if (tmp == nullptr) 
    { 
     lock_guard<mutex> l(_singleton_mutex); 
     tmp = _instance.load(memory_order_relaxed); 
     if (tmp == nullptr) 
     { 
      tmp = new S(); 
      atomic_thread_fence(memory_order_release); 
      _instance.store(tmp, memory_order_relaxed); 
    } 
    return *tmp; 
} 

void S::Dispose() 
{ 
    using namespace std; 
    S * tmp = _instance.load(memory_order_relaxed); 
    atomic_thread_fence(memory_order_acquire); 
    if (tmp != nullptr) 
    { 
     lock_guard<mutex> l(_singleton_mutex); 
     tmp = _instance.load(memory_order_relaxed); 
     if (tmp != nullptr) 
     { 
      atomic_thread_fence(memory_order_release); 
      _instance.store(nullptr, memory_order_relaxed); 
      delete tmp; 
     } 
    } 
} 
+0

'Instance'与[Double-checked_locking](https://en.wikipedia.org/wiki/Double-checked_locking)中提供的相同,应该是正确的。 – Jarod42

+0

顺便说一句,删除空指针是有效的。 – Jarod42

+0

@ Jarod42大声笑感谢的人,我完全忘记了这一点:D我认为这是足够安全的使用在我的情况..所以处置可以简化删除_instance.load(..); ? – Paladin

回答

2

解决方法是:看起来不错。

更多信息:

当它是潜在的可以接受的,你有两个实例一短暂的瞬间,其中第二个会被立即销毁,你可以摆脱互斥的:

std::atomic<S *> S::_instance; 

S& S::Instance() 
{ 
    using namespace std; 
    auto tmp = _instance.load(memory_order_relaxed); 
    if (tmp == nullptr) 
    { 
     auto tmp2 = new S(); 
     if(!_instance.compare_exchange_strong(tmp, tmp2)) 
      delete tmp2; 
    } 
    return *tmp; 
} 

void S::Dispose() 
{ 
    using namespace std; 
    auto tmp = _instance.load(memory_order_relaxed); 
    if (tmp != nullptr) 
    { 
     if(_instance.compare_exchange_strong(tmp, nullptr)) 
      delete tmp; 
    } 
} 

当两个线程同时启动Instance()时,都会看到一个nullptr并创建一个新的S。只有其中一个会成功替换实例指针,而另一个会立即删除新的S。

无论如何,你可能更愿意使用斯科特迈尔斯单身,虽然它并没有提供一种方式来处理对象:

S& S::Instance() 
{ 
    // This instance will be created at first call - thread safe. 
    // it lives until the program terminates. 
    static Singleton instance; 
    return instance; 
} 

这是最优雅的,最少的代码,以及线程安全的,顺便说一句。

+0

一般我同意,除了1)它不允许处理单身人士2)它不是线程安全的Visual Studio 2012 afaik,不知道更新。 – Paladin

1

正如在其他答案中所说的,实现看起来很好。但是,有一个概念问题:用户和实例的处置者之间的竞争条件。

Thread A: var i = s::Instance(); 
Thread B: s::Dispose(); 
Thread A: i.doSth(); 

可能有用例可以保证这种情况不会发生;否则, 引用计数可能是解决此问题的方法。