单例的线程安全实例化 [英] Thread safe instantiation of a singleton

查看:93
本文介绍了单例的线程安全实例化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用哪种同步方法来确保单例保持单例?

Which one synchronization method to use to ensure a singleton remains a singleton?

+(Foo*)sharedInstance
{
   @synchronized(self)
   {
      if (nil == _sharedInstance)
      {
         _sharedInstance = [[Foo alloc] init];
         ...
      }
   }
   return _sharedInstance;
}

还是使用互斥锁?

#import <pthread.h>

static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;

+(Foo*)sharedInstance
{
   pthread_mutex_lock(&_mutex);
   if (nil == _sharedInstance)
   {
      _sharedInstance = [[Foo alloc] init];
      ...
   }
   pthread_mutex_unlock(&_mutex);
   return _sharedInstance;
}

嗯..对此有何评论?

推荐答案

请确保您也阅读了有关此问题/答案的讨论. 为什么要在Objective-C中分开alloc和init调用以避免死锁?

Make sure you read the discussion on this question/answer, too. Why should we separate alloc and init calls to avoid deadlocks in Objective-C?

扩大种族条件问题; real 修复程序是为了在应用程序中进行不确定的初始化. 不确定懒惰初始化导致的行为由于看似无害的更改(配置,不相关"的代码更改等)而可以轻松更改.

To expand on the race condition issue; the real fix is to not have indeterminate initialization within your application. Indeterminate or lazy initialization results in behavior that can easily change due to seemingly innocuous changes -- configuration, "unrelated" code changes, etc...

更好地在程序生命周期中的已知良好点显式初始化子系统. IE.如果确实需要 将子系统中的[MyClass sharedInstance];放入应用程序委托的applicationDidFinishLaunching:方法中,则可以在程序中尽早初始化该子系统(或者,如果希望进一步防御,则可以更早地将其移动).

Better to explicitly initialize subsystems on a known-good point in the program's lifespan. I.e. drop [MyClass sharedInstance]; into your App delegate's applicationDidFinishLaunching: method if you really need that subsystem initialized early in the program (or move it even earlier, if you want to be extra defensive).

最好还是将初始化完全移出该方法. IE. [MyClass initializeSharedInstance];其中,如果未首先调用该方法,则+sharedInstance声明().

Better still to move initialization out of that method entirely. I.e. [MyClass initializeSharedInstance]; where +sharedInstance asserts() if that method isn't called first.

尽管我很喜欢便利,但是25年的ObjC编程使我认识到,懒惰初始化是更多值得维护和重构头痛的原因.

As much as I'm a a fan of convenience, 25 years of ObjC programming has taught me that lazy initialization is a source of more maintenance and refactoring headaches than it is worth.

尽管存在下面描述的竞争条件,但是此代码无法解决下面描述的问题.几十年来,我们不必担心共享实例初始化程序中的并发性.为繁荣留下错误的代码.

请记住,对于Colin和Harald的其他正确答案,都有一个非常微妙的比赛条件可能会使您陷入困境.

Keep in mind that for both Colin's and Harald's otherwise correct answers, there is a very subtle race condition that could lead you to a world of woe.

即,如果要分配的类的-init恰巧调用sharedInstance方法,则它将在设置变量之前执行.在这两种情况下,都将导致死锁.

Namely, if the -init of the class being allocated happens to call the sharedInstance method, it will do so before the variable is set. In both cases it will lead to a deadlock.

这是您要分离alloc和init的一次.因为这是最好的解决方案,所以限制了Colin的代码(假设使用Mac OS X):

This is the one time that you want to separate the alloc and the init. Cribbing Colin's code because it is the best solution (assuming Mac OS X):

+(MyClass *)sharedInstance
{   
    static MyClass *sharedInstance = nil;
    static dispatch_once_t pred;

    // partial fix for the "new" concurrency issue
    if (sharedInstance) return sharedInstance;
    // partial because it means that +sharedInstance *may* return an un-initialized instance
    // this is from https://stackoverflow.com/questions/20895214/why-should-we-separate-alloc-and-init-calls-to-avoid-deadlocks-in-objective-c/20895427#20895427

    dispatch_once(&pred, ^{
        sharedInstance = [MyClass alloc];
        sharedInstance = [sharedInstance init];
    });

    return sharedInstance;
}

注释(仅在Mac OS X上有效);尤其是X 10.6+和iOS 4.0+.在较旧的操作系统上,如果没有可用的块,请使用锁或一旦不基于块的操作就可以执行某种操作.

note this only works on Mac OS X; X 10.6+ and iOS 4.0+, in particular. On older operating systems, where blocks are not available, use a lock or one of the various means of doing something once that isn't blocks based.

上面的模式实际上并不能防止文本中描述的问题,并且在遇到时会导致死锁.问题在于dispatch_once()不能重入,因此,如果init调用sharedInstance,则楔城.

The above pattern does not actually prevent the problem described in the text and will cause a deadlock when it is encountered. The problem is that the dispatch_once() is not re-entrant and, thus, if the init calls sharedInstance, wedge city.

这篇关于单例的线程安全实例化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆