C++ 如何在Windows中创建线程安全的单例模式?

C++ 如何在Windows中创建线程安全的单例模式?,c++,windows,singleton,C++,Windows,Singleton,我一直在阅读线程安全的单例模式: 它在底部说,唯一安全的方法是使用pthread_一次,这在Windows上是不可用的 这是保证线程安全初始化的唯一方法吗 我读过这篇文章,所以: 似乎暗示了一个原子操作系统级的交换和比较功能,我认为在Windows上是: 这能满足我的要求吗 Edit:我希望延迟初始化,并且该类只有一个实例 另一个站点上有人提到在名称空间中使用全局(他将单例描述为反模式)——它怎么可能是“反模式” 接受答案: 我已经接受了,因为我正在使用Visual Studio 2008

我一直在阅读线程安全的单例模式:

它在底部说,唯一安全的方法是使用pthread_一次,这在Windows上是不可用的

这是保证线程安全初始化的唯一方法吗

我读过这篇文章,所以:

似乎暗示了一个原子操作系统级的交换和比较功能,我认为在Windows上是:

这能满足我的要求吗

Edit:我希望延迟初始化,并且该类只有一个实例

另一个站点上有人提到在名称空间中使用全局(他将单例描述为反模式)——它怎么可能是“反模式”

接受答案:
我已经接受了,因为我正在使用Visual Studio 2008-注意:对于未来的读者,如果您没有使用此编译器(或2005)-请不要使用已接受的答案

编辑: 除了return语句之外,代码工作正常-我收到一个错误: 错误C2440:“返回”:无法从“volatile Singleton*”转换为“Singleton*”。 我是否应该将返回值修改为volatile Singleton*?


编辑:显然const\u cast将删除volatile限定符。再次感谢Josh。

在windows上进行线程安全的Singleton*初始化有很多方法。事实上,其中一些甚至是跨平台的。在你链接到的SO线程中,他们正在寻找一个用C语言懒洋洋地构造的单例,这有点具体,而且考虑到你所使用的内存模型的复杂性,要正确地完成这项工作可能会有点棘手

  • 你不应该用它

保证单例的跨平台线程安全初始化的一个简单方法是在应用程序启动任何其他线程之前,在应用程序的主线程中显式地执行它(通过调用单例上的静态成员函数)(或至少任何其他将访问singleton的线程)

然后通过互斥体/关键部分以通常的方式确保对单例的线程安全访问

延迟初始化也可以使用类似的机制来实现。通常遇到的问题是,提供线程安全性所需的互斥体通常在单例中初始化,这只会将线程安全问题推到互斥体/关键部分的初始化。克服此问题的一种方法是要在应用程序的主线程中创建和初始化互斥/关键部分,请通过调用静态成员函数将其传递给singleton。然后,可以使用此预初始化的互斥/关键部分以线程安全的方式对singleton进行重量级初始化。例如:

// A critical section guard - create on the stack to provide 
// automatic locking/unlocking even in the face of uncaught exceptions
class Guard {
    private:
        LPCRITICAL_SECTION CriticalSection;

    public:
        Guard(LPCRITICAL_SECTION CS) : CriticalSection(CS) {
            EnterCriticalSection(CriticalSection);
        }

        ~Guard() {
            LeaveCriticalSection(CriticalSection);
        }
};

// A thread-safe singleton
class Singleton {
    private:
        static Singleton* Instance;
        static CRITICAL_SECTION InitLock;
        CRITICIAL_SECTION InstanceLock;

        Singleton() {
            // Time consuming initialization here ...

            InitializeCriticalSection(&InstanceLock);
        }

        ~Singleton() {
            DeleteCriticalSection(&InstanceLock);
        }

    public:
        // Not thread-safe - to be called from the main application thread
        static void Create() {
            InitializeCriticalSection(&InitLock);
            Instance = NULL;
        }

        // Not thread-safe - to be called from the main application thread
        static void Destroy() {
            delete Instance;
            DeleteCriticalSection(&InitLock);
        }

        // Thread-safe lazy initializer
        static Singleton* GetInstance() {
            Guard(&InitLock);

            if (Instance == NULL) {
                Instance = new Singleton;
            }

            return Instance;
        }

        // Thread-safe operation
        void doThreadSafeOperation() {
            Guard(&InstanceLock);

            // Perform thread-safe operation
        }
};
但是,完全避免使用单例是有充分理由的(以及为什么它们有时被称为反模式):

  • 它们本质上是美化的全局变量
  • 它们可能导致应用程序的不同部分之间的高度耦合
  • 它们会使单元测试变得更加复杂或不可能(因为很难用假实现替换真正的单例)

另一种方法是使用“逻辑单例”,在主线程中创建和初始化一个类的单个实例,并将其传递给需要它的对象。如果有许多对象要创建为单例,这种方法可能会变得很笨拙。在这种情况下,可以将不同的对象捆绑到一个单例中调用“Context”对象,然后在必要时传递该对象。

您可以使用操作系统原语(如互斥体或临界区)来确保线程安全初始化,但是每次访问单例指针时(由于获取锁),这将导致开销这也是不可移植的。

< P>有一个澄清点你需要考虑这个问题。你需要……/P>
  • 实际上只创建了一个类的实例
  • 可以创建一个类的多个实例,但该类应该只有一个真正的最终实例

  • 网络上有很多示例来实现C++中的这些模式。这里是

    < p>下面解释了如何在C语言中实现它,但是完全相同的概念适用于支持单模式

    的任何编程语言。

    您需要决定是否需要延迟初始化。延迟初始化意味着单例中包含的对象是在第一次调用它时创建的 例:

    如果该调用直到稍后才进行,那么正如本文所解释的,线程之间存在争用条件的危险

    MySingleton::getInstance()->initSingleton();
    

    在代码的一开始,假设它是线程安全的,那么您就不再懒惰初始化,您将需要“一些”当你的应用程序启动时,处理能力更强。但是,如果你这样做,它将解决很多关于种族条件的问题。

    < P>如果你使用Visual C++ 2005/2008,你可以使用双检查锁定模式,因为“”,这是实现一个懒惰的初始化单体的最有效的方法。 从

    Singleton*GetSingleton()
    {
    易失性静态单态*p单态=0;
    如果(pSingleton==NULL)
    {
    肠危重科(&cs);
    如果(pSingleton==NULL)
    {
    尝试
    {
    pSingleton=新的Singleton();
    }
    捕获(…)
    {
    //出了点问题。
    }
    }
    离开关键部门(&cs);
    }
    返回常数(pSingleton);
    }
    
    每当需要访问singleton时,只需调用GetSingleton()。第一次调用它时,静态指针将是init
    MySingleton::getInstance()->initSingleton();
    
    Singleton* GetSingleton()
    {
        volatile static Singleton* pSingleton = 0;
    
        if (pSingleton == NULL)
        {
            EnterCriticalSection(&cs);
    
            if (pSingleton == NULL)
            {
                try
                {
                    pSingleton = new Singleton();
                }
                catch (...)
                {
                    // Something went wrong.
                }
            }
    
            LeaveCriticalSection(&cs);
        }
    
        return const_cast<Singleton*>(pSingleton);
    }
    
    struct X { };
    
    X * get_X_Instance()
    {
        static X x;
        return &x;
    }
    extern int X_singleton_helper = (get_X_instance(), 1);