C++ 在初始化完成之前对singleton调用方法

C++ 在初始化完成之前对singleton调用方法,c++,singleton,C++,Singleton,在将旧的VisualStudio2003项目转换为2015年时,我遇到了一个问题,应用程序在启动后立即冻结。我似乎无法从调试器中获得太多信息,因为应用程序并没有真正崩溃 当我暂停调试器时,它总是指向同一行代码,即singleton GetInstance方法中的静态变量。这使应用程序看起来好像在等待初始化。此对象的构造函数正在调用使用相同GetInstance方法的方法,因此在构造函数结束之前正在使用该实例 这就是代码的工作原理: //A.cpp A::A() { B::GetInsta

在将旧的VisualStudio2003项目转换为2015年时,我遇到了一个问题,应用程序在启动后立即冻结。我似乎无法从调试器中获得太多信息,因为应用程序并没有真正崩溃

当我暂停调试器时,它总是指向同一行代码,即singleton 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();
}
我知道这段代码是可以改进的,有很多方法可以解决它,但我想知道为什么这段代码在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上尝试了这一点。看起来代码深入到了未定义的行为中(
    实例
    是在初始化完成之前使用的),然后设法恢复正常。换句话说,您运气不好,代码被破坏了,看起来好像正常工作了。