在初始化完成之前对单例调用方法

时间:2016-10-27 22:43:11

标签: c++ singleton

将旧的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中中断。

提前致谢。

1 个答案:

答案 0 :(得分:2)

@Lehu在他们删除的答案中大多是正确的。它是一个循环引用,如果没有被互斥锁阻塞,它将递归。这里发生了什么:

  1. B::DoSomething()名为
  2. B::DoSomething()调用A::GetInstance()
  3. A::GetInstance()调用构造函数A::A()来构造static A instance。这将锁定instance创建的关键部分,以确保可以在不中断的情况下完成作业。
  4. A::A()调用B::DoSomething();查看圈子形成?
  5. B::DoSomething()调用A::GetInstance()
  6. A::GetInstance()尝试访问instance,但instance尚未完成构建,因此执行会停止,直到可以。
  7. 不幸的是,第3步无法完成,直到第6步完成。这是一个典型的死锁:3正在等待6完成,6正在等待3完成。 To Quote Pvt. Hudson, "Game over, man! Game over!"

    编辑:考虑到这一点,互斥量并不是一个非常正确的术语。获取静态变量instance的{​​{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();
    }