为什么要在Objective-C中分开alloc和init调用以避免死锁? [英] Why should we separate alloc and init calls to avoid deadlocks in Objective-C?

查看:77
本文介绍了为什么要在Objective-C中分开alloc和init调用以避免死锁?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在阅读有关线程安全单例的信息时,我发现单例的线程安全实例化在此处,并在可接受的答案中输入以下代码:

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方法,则它将在设置变量之前执行.在这两种情况下,都将导致死锁.这是您要分开allocinit的一次.

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. This is the one time that you want to separate the alloc and the init.

有人可以详细向我解释这种分离的好处吗?我根本不明白作者的意思.创建单例时,即使在线程安全的dispatch_once()中执行此操作,我真的真的需要将allocinit方法调用分开吗?

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屋!

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