单例的线程安全实例化 [英] Thread safe instantiation of a singleton
问题描述
使用哪种同步方法来确保单例保持单例?
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屋!