我有以下课程
class Singleton
{
private:
static Singleton *p_inst;
Singleton();
public:
static Singleton * instance()
{
if (!p_inst)
{
p_inst = new Singleton();
}
return p_inst;
}
};
请详细说明在多线程环境中实施Singleton时采取的预防措施。
答案 0 :(得分:7)
在多线程条款
中if(!p_inst)
{
p_inst = new Singleton();
}
实际上是3个单独的动作。您获得p_inst
的值,设置p_inst
的值并写入p_inst
的值。因此,get-set-write意味着您需要锁定p_inst
,否则您可以拥有2个线程,这些线程创建每个线程使用的Singleton
值。
以下是您查看问题的方法,假设您的Singleton
有一个可变字段val
:
thread A -> p_inst is NULL
thread B -> p_inst is NULL
thread A -> set to Singleton (1)
thread B -> set to Singleton (2)
thread C -> p_inst is Singleton (2)
thread A -> set val to 4
thread B -> set val to 6
thread C -> get val (it's 6)
thread A -> get val (it's 4!!)
你知道吗?有一个Singleton浮动的副本,其中两个都不知道另一个。检查Singleton
的第三个线程只会看到最后一个分配。但是通过锁定,您可以防止多次分配和这些类型的问题。
答案 1 :(得分:5)
在分配或读取指针之前,你必须使用互斥锁并锁定指针,这使得这个设计模式很慢(而且非常简单)。
答案 2 :(得分:4)
我将简要说明:这取决于你的编译器。
现在,你必须意识到你可能不需要这个。
有两种方法可以解决这个问题,不需要任何多线程感知。
static
实例,而不是动态分配它。安全而简单。如果您需要从另一个static
变量main
调用它。当然,真正的问题是:你不能只是传递对象的引用而不是创建一个全局变量吗?它会使测试变得更容易;)
答案 3 :(得分:4)
您可以通过在启动多个线程之前简单地分配(以任何方式选择)此类对象来消除所有问题。由于设计约束(在静力学中使用单例,你需要延迟分配等),这可能并不总是可行的,但它很简单,并且可以控制创建顺序。有时跟踪有关此类对象分配顺序和时间的问题是一件很容易避免的麻烦。
P.S。 - 我知道这并没有直接回答你的问题,但它可能是一个没有复杂性的真正问题的实际解决方案。
答案 4 :(得分:1)
对于多线程构造,请在instance()函数中使用静态变量。静态变量的初始化由编译器自动保护。任何其他操作都需要显式锁定。使用互斥锁。
class Singleton
{
private:
Singleton();
public:
static Singleton * instance()
{
static Singleton inst;
return &inst;
}
};
答案 5 :(得分:1)
你应该问问自己线程安全的意思。
你的单身人员真的需要线程安全吗?
如果没有,请考虑线程静态方法
您是否希望保证不会创建单身的两个实例?
如果没有,您的上述解决方案可能没问题,没有任何锁定:您在施工时遇到了竞争条件 - 但是您不关心因为最终只有一个会存活 - 但是,除非您小心,否则您可能会有资源泄漏,这可能会也可能不会很重要。 (这实际上是一个缓存)。
您是否希望保证最终只剩下一个实例?
您是否关心锁定成本?
如果不是(这很常见),你可以锁定它并开心。
单例是一种可以解决各种问题的模式 - 但是需要什么样的线程安全性与单例模式本身无关,而与你想要的一切有关。