在初始化完成之前调用单例上的方法
Calling method on singleton before initialization finished
当将旧的Visual Studio 2003项目转换为2015时,我遇到了一个问题,即应用程序在启动后立即冻结。我似乎无法从调试器中获得太多信息,因为应用程序并没有真正崩溃。
当我暂停调试器时,它总是指向同一行代码,即单例GetInstance方法中的静态变量。这使它看起来像应用程序正在等待它被初始化。该对象的构造函数正在调用一个使用相同GetInstance方法的方法,因此该实例在构造函数结束之前被使用。
代码是这样工作的:
//A.cpp
A::A()
{
B::GetInstance()->DoSomething();
}
A* A::GetInstance()
{
static A instance; // The debugger always points out that this line is next to be executed after pausing the application
return &instance;
}
//B.cpp
void B::DoSomething()
{
A::GetInstance()->DoSomethingElse();
}
我知道这段代码可以改进,有办法解决它,但我想知道为什么这段代码在Visual Studio 2003中工作得很好,而在Visual Studio 2015中却中断了。
@Lehu删除的答案基本上是正确的。它是一个循环引用,如果没有被互斥锁阻塞,它就会递归。结果如下:
-
B::DoSomething()
被称为 -
B::DoSomething()
调用A::GetInstance()
-
A::GetInstance()
调用A::A()
构造static A instance
。这将锁定instance
创建周围的临界区,以确保可以在不中断的情况下完成作业。 -
A::A()
调用B::DoSomething();
看到圆形成了吗? -
B::DoSomething()
调用A::GetInstance()
-
A::GetInstance()
试图访问instance
,但instance
尚未完成构建,因此执行暂停,直到它可以。
不幸的是,步骤3不能完成,直到步骤6完成。这是一个典型的死锁:3在等待6完成,6在等待3完成。引用哈德逊大兵的话:"游戏结束了,伙计!游戏结束!"
编辑:考虑到这一点,互斥不是很正确的术语。在获取静态变量instance
周围设置一个临界区更为合适。
我的MCVE演示:
struct A
{
A();
static A* GetInstance()
{
static A instance;
return &instance;
}
void DoSomethingElse()
{
}
};
struct B
{
void DoSomething()
{
A::GetInstance()->DoSomethingElse();
}
static B* GetInstance()
{
static B instance;
return &instance;
}
};
A::A()
{
B::GetInstance()->DoSomething();
}
int main()
{
B::GetInstance()->DoSomething();
}
相关文章:
- 为什么在单例中,检查类==空?
- C++ 实现模板单例类时出现链接错误
- 在类中存储单例的指针
- C++中的单例实现在调用 getInstance 函数时不会产生相同的类实例
- 具有非默认构造函数的单例类
- 使用 std::call_once 实现类似单例的功能
- 为什么单例使用指针而不是引用?
- 设计许多单例代码结构的更好方法
- 双重检查创建单例问题的方法
- 如何在静态单例类中以编程方式从exec方法返回
- GDB 在单步执行单例的静态方法时没有显示这一点?
- 这是在C++中创建单例类的正确方法吗?
- 使用虚拟方法组织单例的最佳方式
- 为什么我们在C++的单例模式中使用静态方法和静态函数
- 为什么C++单例实例化需要do_nothing方法
- 有没有一种方法可以为本地作用域中的单例分配名称
- 这种c++单例模式和方法公开是一种好的实践吗?
- c++, linux,如何将非静态方法从单例传递给pthread_create
- 创建模板方法的最佳方式(对于单例类),它接收模板类作为参数
- 在初始化完成之前调用单例上的方法