代码之家  ›  专栏  ›  技术社区  ›  user152508

单点死参考问题

  •  3
  • user152508  · 技术社区  · 14 年前

    我读了很多关于单身的书。我在想单子之间的死参考问题。在网络上的每一个primer中,当一个singleton在其析构函数中调用另一个singleton,并且该singleton已经被销毁时,就会遇到这个问题,例如,可以从许多其他singleton的析构函数中调用log singleton。

    我无法想象在其他情况下(除了在DTR中引用其他单例),死掉的引用会是一个问题。你能给我一个现实世界中存在这样一个问题的例子吗?我该如何解决它?

    问题是,我需要在我们的项目中实现几个单例,它们都是相互通信的,我很难选择正确的方式。请不要说不要使用单件,因为这不是我的决定。

    6 回复  |  直到 14 年前
        1
  •  1
  •   Community CDub    8 年前

    从这里复制: Finding C++ static initialization order problems (没人会只关注链接抱歉)

    另请参阅本文: C++ Singleton design pattern

    破坏问题:

    在对象被破坏后,有一个访问对象的潜在问题。只有从另一个全局变量的析构函数访问对象时才会发生这种情况(通过全局,我指的是任何非局部静态变量)。

    解决办法你必须确保你强制破坏秩序。
    记住,破坏的顺序与构造的顺序完全相反。因此,如果访问析构函数中的对象,必须确保该对象没有被破坏。要做到这一点,您必须确保在构造调用对象之前,对象已经完全构造好了。

    class B
    {
        public:
            static B& getInstance_Bglob;
            {
                static B instance_Bglob;
                return instance_Bglob;;
            }
    
            ~B()
            {
                 A::getInstance_abc().doSomthing();
                 // The object abc is accessed from the destructor.
                 // Potential problem.
                 // You must guarantee that abc is destroyed after this object.
                 // To gurantee this you must make sure it is constructed first.
                 // To do this just access the object from the constructor.
            }
    
            B()
            {
                A::getInstance_abc();
                // abc is now fully constructed.
                // This means it was constructed before this object.
                // This means it will be destroyed after this object.
                // This means it is safe to use from the destructor.
            }
    };
    
        2
  •  3
  •   JoeG    14 年前

    破坏顺序问题是单件模式的一部分。

    请不要说不要用 辛格尔顿,因为那不是我的 决定。

    不使用它们是正确的事情-鉴于这是不可能的,你将不得不使用一个黑客解决方案。这里有一些可能的解决方案,但没有一个是很好的:

    • 不要引用析构函数中的其他单例
    • 在主目录末尾以正确的顺序显式地销毁单例
    • 让你的单件收藏其他单件的参考资料 weak_ptr -它们可以彼此独立地销毁,并且您可以在使用之前安全地检查引用的单例是否仍然存在。

    另外,我建议不要在多线程上下文中创建或销毁单例——要确保所有单例都是在任何新线程之前创建的,并且除主线程之外的所有线程在销毁前都已停止,这要容易得多。

        3
  •  1
  •   MSalters    14 年前

    哑铃编码器已经把你指向正确的方向。在现代C++设计中,Andrei Alexandrescu用单体解释了复杂的设计问题,并根据单体的精确要求显示了多个解决方案。

    不过,它并不是所有可能的单例实现的完整指南。您应该阅读它不是为了代码,而是为了理解分析。把你学到的知识应用到你的特定情况。

    为了回答您的特定问题,另一种常见的“死”引用的情况更好地称为“未出生的引用”——在运行其构造函数之前使用单例。但是应该很明显,因为独生子生活在一个程序的大部分生命周期中,它们唯一不存在的两次是在一个程序的开始和结束的时候。

        4
  •  1
  •   Tony Delroy    14 年前

    我没有看到上面提到的一种可能性,这可能是可以接受的,也可能是不可以接受的,这取决于它们管理的是什么:在堆上分配单例,不要破坏它们…只要让操作系统在应用程序终止时回收它们持有的所有描述符/内存/锁等(注意:并不是所有东西都能用,例如共享内存中的锁)。

        5
  •  0
  •   utnapistim    14 年前

    据我所知,单例是按照您第一次调用访问函数的顺序创建的,并以相反的顺序销毁。

    因此,您可以为您的应用程序创建一个in it函数(并确保它是在您的主函数中调用的第一件事)。

    在这个函数中,按照希望创建的顺序调用单例访问函数。

        6
  •  0
  •   Matthieu M.    14 年前

    我们缺乏信息,最重要的是我希望我们正在谈论C++0X,否则将是相当困难的。

    第一个解决方案是显式管理您的单例。在Web上遇到的大多数设计都集中在简单性和易用性上,而在一般情况下则以正确性为代价。

    最简单的方法是在您仍然是单线程的情况下(为了避免同步问题)以正确的顺序实例化它们并释放它们,以避免单线程之间的相互依赖。

    这很自然地遵循了 Singleton Manager 这是某种“超级单例”,将实例化您的单例并相应地释放它们。对单例的所有访问都是通过它来完成的,这样可以确保访问时它们是活动的。再一次,创建和破坏发生在单线程的情况下。

    当我们谈论懒惰的初始化(按需)时,它变得更加困难。最简单的方案是局部静态变量:

    MySingleton& Get() { static MySingleton M; return M; }
    

    C++0X最终保证只有一个实例 MySingleton 将被实例化,使事情变得更容易!不过,这里确实存在“死参考”问题。

    在C++中,静态对象的销毁顺序与构造顺序正好相反,因此一种解决方案是强制在构造函数中使用单个单元格的析构函数中使用的任意单元格(所有这些)。这样你才能真正保证它会在以前建造,然后在以后被摧毁。

    请注意,在C++ 03中的多线程环境中(或更早),惰性实例化是困难的,因为不能保证创建一个实例。在那一点上获取一个锁是非常困难的(毕竟,互斥体本身就是一个单体的…不是吗?).