2015-11-10 197 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(); 

可能有用例可以保證這種情況不會發生;否則, 引用計數可能是解決此問題的方法。