c++ - java单例线程安全 - 我如何在Windows中创建一个线程安全的单例模式?




singleton (6)

下面解释了如何在C#中完成它,但是完全相同的概念适用于任何支持单例模式的编程语言

http://www.yoda.arachsys.com/csharp/singleton.html

你需要决定什么是你想懒惰初始化或不。 惰性初始化意味着包含在单例中的对象是在第一次调用它时创建的。例如:

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

如果这个调用不是直到以后才进行的话,线程之间的竞争状态就有危险,正如文章中所解释的那样。 但是,如果你把

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

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

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

http://en.wikipedia.org/wiki/Singleton_pattern#C.2B.2B_.28using_pthreads.29

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

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

我已经读过这个线程:

用C ++线程安全地构建一个单例

而且似乎暗示了一个原子操作系统级别的交换和比较函数,我在Windows上假设的是:

http://msdn.microsoft.com/en-us/library/ms683568.aspx

这可以做我想要的吗?

编辑:我想懒惰的初始化,并为此只有一个类的实例。

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

一般承认的答案:
我已经接受Josh的答案,因为我正在使用Visual Studio 2008 - NB:对于未来的读者,如果你不使用这个编译器(或2005) - 不要使用接受的答案!

编辑: 代码工作正常,除了返回语句 - 我得到一个错误:错误C2440:'返回':不能从'易变的单身*'转换为'单身人士*'。 我应该修改返回值是易变的Singleton *?

编辑:显然const_cast <>将删除挥发性限定符。 再次感谢乔希。


对于这个问题,你需要考虑一个问题。 你需要...

  1. 一个类的唯一一个实例是实际创建的
  2. 可以创建一个类的许多实例,但只能有一个真正的类的确定实例

网上有很多样本用C ++实现这些模式。 这是一个代码项目示例


有很多方法可以在Windows上进行线程安全的Singleton *初始化。 其实有些甚至是跨平台的。 在你链接到的SO线程中,他们正在寻找一个在C中被懒惰地构造的单例,这是一个更具体一点,考虑到你正在工作的内存模型的复杂性,可能有点棘手。

  • 你永远不应该使用它

保证单例的跨平台线程安全初始化的一个简单方法是应用程序启动任何其他线程之前(在至少一个单线程中调用静态成员函数)任何其他线程将访问单身人士)。

通过互斥/关键部分的常用方式确保线程安全访问单例。

懒惰初始化也可以使用类似的机制来实现。 遇到的常见问题是,提供线程安全所需的互斥体通常在单例本身中初始化,这只是将线程安全问题推到了互斥/临界部分的初始化。 解决这个问题的一种方法是在应用程序的主线程中创建并初始化互斥/关键部分,然后通过调用静态成员函数将其传递给单例。 然后,使用这个预初始化的互斥/关键部分,可以以线程安全的方式发生单身人士的重量级初始化。 例如:

// 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
        }
};

然而,完全避免使用单身人士是有充分理由的(为什么他们有时被称为反模式 ):

  • 他们本质上是荣耀的全球变数
  • 它们可能导致应用程序不同部分之间的高度耦合
  • 他们可以使单元测试变得更加复杂或不可能(因为用真实的实现来交换真实的单例很困难)

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


如果你正在寻找一个更便携,更简单的解决方案,你可以转向提升。

boost :: call_once可以用于线程安全初始化。

它非常简单易用,并将成为下一个C ++ 0x标准的一部分。


虽然我喜欢公认的解决方案,但我还是发现了另一个有希望的领导,并认为我应该在这里分享一下: 一次初始化(Windows)





singleton