单线程实现线程安全吗?
问题描述:
单线程实现线程安全吗? :: 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;
}
}
}
答
解决方法是:看起来不错。
更多信息:
当它是潜在的可以接受的,你有两个实例一短暂的瞬间,其中第二个会被立即销毁,你可以摆脱互斥的:
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
答
正如在其他答案中所说的,实现看起来很好。但是,有一个概念问题:用户和实例的处置者之间的竞争条件。
Thread A: var i = s::Instance();
Thread B: s::Dispose();
Thread A: i.doSth();
可能有用例可以保证这种情况不会发生;否则, 引用计数可能是解决此问题的方法。
'Instance'与[Double-checked_locking](https://en.wikipedia.org/wiki/Double-checked_locking)中提供的相同,应该是正确的。 – Jarod42
顺便说一句,删除空指针是有效的。 – Jarod42
@ Jarod42大声笑感谢的人,我完全忘记了这一点:D我认为这是足够安全的使用在我的情况..所以处置可以简化删除_instance.load(..); ? – Paladin