C++ 在初始化完成之前对singleton调用方法
在将旧的VisualStudio2003项目转换为2015年时,我遇到了一个问题,应用程序在启动后立即冻结。我似乎无法从调试器中获得太多信息,因为应用程序并没有真正崩溃 当我暂停调试器时,它总是指向同一行代码,即singleton GetInstance方法中的静态变量。这使应用程序看起来好像在等待初始化。此对象的构造函数正在调用使用相同GetInstance方法的方法,因此在构造函数结束之前正在使用该实例 这就是代码的工作原理:C++ 在初始化完成之前对singleton调用方法,c++,singleton,C++,Singleton,在将旧的VisualStudio2003项目转换为2015年时,我遇到了一个问题,应用程序在启动后立即冻结。我似乎无法从调试器中获得太多信息,因为应用程序并没有真正崩溃 当我暂停调试器时,它总是指向同一行代码,即singleton GetInstance方法中的静态变量。这使应用程序看起来好像在等待初始化。此对象的构造函数正在调用使用相同GetInstance方法的方法,因此在构造函数结束之前正在使用该实例 这就是代码的工作原理: //A.cpp A::A() { B::GetInsta
//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();
}
我知道这段代码是可以改进的,有很多方法可以解决它,但我想知道为什么这段代码在VisualStudio2003中运行良好,而在VisualStudio2015中运行中断
提前谢谢。@Lehu删除的答案基本上是正确的。它是一个循环引用,如果没有被互斥锁阻止,它将是递归的。下面是发生的情况:
B::DoSomething()
调用B::DoSomething()
调用A::GetInstance()
A::GetInstance()
调用构造函数A::A()
来构造静态实例。这将锁定创建实例
的关键部分,以确保作业可以在不中断的情况下完成
A::A()
调用B::DoSomething()代码>看到圆形成了吗
B::DoSomething()
调用A::GetInstance()
A::GetInstance()
尝试访问实例
,但实例
尚未完成构造,因此执行将暂停,直到可以访问为止李>
不幸的是,在步骤6完成之前,步骤3无法完成这是一个典型的死锁:3等待6完成,6等待3完成
编辑:仔细想想,互斥不是很合适的术语。获取静态变量实例
更合适
我要展示的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();
}
这是静态初始化“互斥”上的一个简单死锁。您在类外初始化了静态变量了吗?还有为什么不
静态一个*实例
。另外,您的GetInstance
不是使用静态实例的static
@macroland代码>是个好主意。C++11保证函数局部静态变量初始化是线程安全的。@Lehu:这通常是通过使用锁或pthread_一次或类似方法来完成的,如果构造函数直接或间接调用GetInstance(),则会出现死锁。这正是上面的代码所发生的事情。@Lehu的答案看起来基本正确。如果不是BUG,那么肯定是BUG。只是需要一个更好的循环解释。谢谢你的解释!这在VisualStudio2003中能起作用有什么特别的原因吗?我在升级过程中没有更改这些类,而且它以前工作得很好。@ExplodingSoap死锁源于多线程环境中对静态变量的保护。C++没有真正承认线程的存在,直到C++ 11标准,所以在MSVC开始拾取C++ 11的碎片在MSVC 2010中,并且可能直到后来才自动处理这个案例是不好的。我的hack-MCVE不应该在旧版本的VisualStudio下工作,因为它会被锁定在递归陷阱中。您的实际实现可能会有所不同。@ExplodingSoap在MSVC 2010上尝试了这一点。看起来代码深入到了未定义的行为中(实例
是在初始化完成之前使用的),然后设法恢复正常。换句话说,您运气不好,代码被破坏了,看起来好像正常工作了。