为什么要在Objective-C中分开alloc和init调用以避免死锁? [英] Why should we separate alloc and init calls to avoid deadlocks in Objective-C?
问题描述
在阅读有关线程安全单例的信息时,我发现单例的线程安全实例化在此处,并在可接受的答案中输入以下代码:
When reading about thread-safe singletons I found Thread safe instantiation of a singleton here on SO, and in the accepted answer this code:
sharedInstance = [MyClass alloc];
sharedInstance = [sharedInstance init];
为什么我们应该分开alloc和init方法?答案的作者写道:
Why should we separate alloc and init methods? The author of the answer wrote:
即,如果要分配的类的
init
恰巧调用sharedInstance
方法,则它将在设置变量之前执行.在这两种情况下,都将导致死锁.这是您要分开alloc
和init
的一次.
Namely, if the
init
of the class being allocated happens to call thesharedInstance
method, it will do so before the variable is set. In both cases it will lead to a deadlock. This is the one time that you want to separate thealloc
and theinit
.
有人可以详细向我解释这种分离的好处吗?我根本不明白作者的意思.创建单例时,即使在线程安全的dispatch_once()
中执行此操作,我真的真的需要将alloc
和init
方法调用分开吗?
Can someone please explain to me in detail what the benefits of this separation are? I couldn't understand what the author meant at all. Do I really need to separate alloc
and init
methods calls when I create a singleton, even if I do it in dispatch_once()
which is thread safe??
推荐答案
@bbum的帖子已更新为提及此解决方案不能解决所描述的问题.不管是否将+alloc
和-init
分开,此问题仍然存在.
@bbum's post has been updated to mention that this solution does not solve the problem being described. Regardless of whether you separate +alloc
and -init
or not, this problem still exists.
推理是在对其帖子的编辑中,但要对此进行扩展,dispatch_once()
不是
The reasoning is in the edit to his post, but to expand on that, dispatch_once()
is not reentrant. In this case, this means that calling dispatch_once()
inside a dispatch_once()
block (ie. recursively) will result in a deadlock.
例如,如果您具有+sharedInstance
的以下代码:
So for example, if you have the following code for +sharedInstance
:
+ (MyClass *)sharedInstance
{
static MyClass *sharedInstance = nil;
static dispatch_once_t pred;
dispatch_once(&pred, ^{
sharedInstance = [[MyClass alloc] init]
});
return sharedInstance;
}
.. and MyClass
的-init
方法也直接或间接调用其自身的+sharedInstance
类方法(例如,其他某些对象,而MyClass -init
可以通过MyClass
的+sharedInstance
分配调用) >),这意味着您正在尝试从自身内部调用dispatch_once
.
..and MyClass
's -init
method directly or indirectly also calls through to its own +sharedInstance
class method (e.g. maybe some other object that MyClass -init
allocates calls through to MyClass
's +sharedInstance
), that would mean that you are attempting to call dispatch_once
from inside itself.
由于dispatch_once
是线程安全的,同步的,并且设计为可以精确执行一次 ,因此您无法在内部块完成一次执行之前再次调用dispatch_once
.这样做会导致死锁,因为dispatch_once
的第二个调用将等待第一个调用(已经在执行过程中)完成,而第一个调用正在等待对
Since dispatch_once
is thread safe, synchronous, and designed such that it executes exactly once, you can not invoke dispatch_once
again before the block inside has finished executing once. Doing so will result in a deadlock, because the second call of dispatch_once
will be waiting for the first call (already in the middle of execution) to complete, while the first call is waiting on the second (recursive) call to dispatch_once
to go through. They are waiting on each other, hence there's a deadlock.
If you want a solution that provides reentrancy, you would need to use something like NSRecursiveLock
which is considerably more expensive than dispatch_once
, which doesn't use a locking mechanism.
根据要求对@bbum原始答案中的+alloc
/-init
进行分割的原因:
Reasoning for the split of +alloc
/-init
in @bbum's original answer as requested:
在编辑之前发布的原始代码@bbum看起来像这样:
The original code @bbum posted before editing it looked like this:
+ (MyClass *)sharedInstance
{
static MyClass *sharedInstance = nil;
static dispatch_once_t pred;
if (sharedInstance) return sharedInstance;
dispatch_once(&pred, ^{
sharedInstance = [MyClass alloc];
sharedInstance = [sharedInstance init];
});
return sharedInstance;
}
请注意以下行:if (sharedInstance) return sharedInstance;
这里的想法是,在调用-init
之前为sharedInstance
分配一个非nil值会导致之前返回现有的sharedInstance
值(从+alloc
返回).在-init
调用导致对+sharedInstance
的递归调用的情况下,击中dispatch_once()
调用(并避免死锁).
The idea here is that assigning a non-nil value to sharedInstance
before calling -init
would result in the existing value of sharedInstance
(returned from +alloc
) being returned before hitting the dispatch_once()
call (and avoiding the deadlock) in the case that the -init
call results in a recursive call to +sharedInstance
as discussed earlier in my answer.
但是,这是一个脆弱的修复,因为if
语句不是线程安全的.
However, this is a brittle fix because the if
statement there is not thread-safe.
这篇关于为什么要在Objective-C中分开alloc和init调用以避免死锁?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!