使用线程安全的Singleton初始化代码停止执行代码 [英] Code execution stops on using thread safe Singleton initialization code

查看:49
本文介绍了使用线程安全的Singleton初始化代码停止执行代码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了利用全局变量和方法,我将Singleton实施为健康的编码实践.我遵循了 Apple文档约翰·华兹华斯博客.首先,我没有使我的单例线程安全,而是将这种方法与博客和Apple文档中提到的所有其他方法一起实现.

To make use of global variables and methods I implemented Singleton as a healthy coding practice. I followed Apple documents, john wordsworth blog before implementing it. In the first, I did not make my singleton thread safe and I implemented this method along with all other mentioned in the blog and Apple document.

+ (SingletonClass *)sharedManager 
{
  static SingletonClass *sharedManager = nil;
  if (sharedManager == nil) {
    sharedManager = [[super allocWithZone:NULL] init];
}
  return sharedManager;
}

在那之后,为了使Singleton线程安全,我像这样对 +(SingletonClass *)sharedManager 类进行了更改,我的应用程序停止启动.我放置了断点,并观察到 dispatch_once 被调用了两次,然后代码停止了进一步执行.

After that to make Singleton thread safe I made changes to + (SingletonClass *)sharedManager class like this and my app stops launching. I put break points and observed dispatch_once gets called twice and then code stops executing further.

+(SingletonClass *)sharedManager
{
  static SingletonClass *sharedManager = nil;
  if (sharedManager !=nil)
  {
    return sharedManager;
  }
  static dispatch_once_t pred;       
  dispatch_once(&pred, ^{
    sharedManager = [SingletonClass alloc];
    sharedManager=[sharedManager init];
});

     return sharedManager;
}

如果我删除了该线程安全代码段,并恢复为先前的代码,它将正常工作并执行代码.

If i remove this thread safe code snippet and revert back to previous code it works fine and code gets executed.

请注意,我还查看了 bbum在这里的答案,其中他在提出问题之前已经提到了可能出现的僵局,但是我无法弄清楚问题所在.任何解释或解决方案将对我有所帮助.谢谢.

Please note that I also looked at the bbum's answer here in which he has mentioned possible deadlock situation before asking question but I am not able to figure out the issue. Any explanation or solution will be helpful for me. Thanks.

万一有人想看完整的代码,我创建了 要领 .请跟随那里.谢谢.

In case someone wants to look at the complete code, I have created gist for that. Please follow there. Thanks.

推荐答案

让我们考虑一下,如果两个线程几乎同时调用 sharedManager 的第二版,会发生什么情况.

Let's consider what happens if two threads call second version of sharedManager almost simultaneously.

线程1首先调用.它检查 sharedManager!= nil ,这是错误的,因此继续进行 dispatch_once .在 dispatch_once 块中,它执行 [SingletonClass alloc] ,并将结果存储在 sharedManager 中.

Thread 1 calls first. It checks sharedManager !=nil, which is false, so it goes on to the dispatch_once. In the dispatch_once block, it executes [SingletonClass alloc] and stores the result in sharedManager.

现在,在线程1继续到下一行之前,线程2出现并调用 sharedManager .线程2检查 sharedManager!= nil ,现在是正确的.因此它返回 sharedManager ,然后调用方尝试使用 sharedManager .但是目前, sharedManager 尚未完全初始化.不好.

Now, before thread 1 continues on to the next line, thread 2 comes along and calls sharedManager. Thread 2 checks sharedManager !=nil, which is now true. So it returns sharedManager, and the caller then tries to use sharedManager. But at this time, sharedManager hasn't been fully initialized yet. That's bad.

在设置完全初始化对象之前,您无法设置 sharedManager .另外(如borrrden所指出的),您不需要在顶部进行 sharedManager!= nil 检查,因为 dispatch_once 仍然非常有效.

You cannot set sharedManager until you have a fully initialized object to set it to. Also (as borrrden pointed out), you don't need the sharedManager !=nil check at the top, because dispatch_once is very efficient anyway.

+ (SingletonClass *)sharedManager {
    static dispatch_once_t pred;
    static SingletonClass *sharedManager;
    dispatch_once(&pred, ^{
        sharedManager = [[SingletonClass alloc] init];
    });
    return sharedManager;
}

现在,我已经查看了您的要点,而您的问题在这里:

Now, I've looked at your gist and your problem is here:

+ (id)allocWithZone:(NSZone*)zone {
    return [[self sharedManager] retain];
}

您的 + [SingletonClass sharedManager] 方法在 dispatch_once 块中调用 + [SingletonClass alloc] .由于您不覆盖 alloc ,因此 + [SingletonClass alloc] 会调用 + [SingletonClass allocWithZone:NULL] .并且 + [SingletonClass allocWithZone:] 方法调用 + [SingletonClass sharedManager] .在第二次调用 sharedManager 时,您的程序挂在 dispatch_once 中,因为您仍在第一次调用 dispatch_once 之内.

Your +[SingletonClass sharedManager] method calls +[SingletonClass alloc] in the dispatch_once block. Since you don't override alloc, +[SingletonClass alloc] calls +[SingletonClass allocWithZone:NULL]. And +[SingletonClass allocWithZone:] method calls +[SingletonClass sharedManager]. On this second call to sharedManager, your program hangs in dispatch_once, because you're still inside the first call to dispatch_once.

最简单的解决方法是删除 allocWithZone:的实现.只需证明 sharedManager 是获取 SingletonClass 实例并继续前进的唯一受支持的方法.

The simplest fix is to remove your implementation of allocWithZone:. Just document that sharedManager is the only supported way to get an instance of SingletonClass and move on.

如果您想变钝并使 [[[SingletonClass alloc] init] 返回单例,即使您反复执行此操作,也很复杂.不要尝试覆盖 alloc allocWithZone:.这样做:

If you want to be obtuse and make [[SingletonClass alloc] init] return the singleton, even if you do it repeatedly, it's complicated. Don't try to override alloc or allocWithZone:. Do this:

static SingletonClass *sharedManager; // outside of any method

+ (SingletonClass *)sharedManager {
    return sharedManager ? sharedManager : [[SingletonClass alloc] init];
}

- (id)init {
    static dispatch_once_t once;
    dispatch_once(&once, ^{
        if (self = [super init]) {
            // initialization here...
            sharedManager = self;
        }
    });
    self = sharedManager;
    return self;
}

这篇关于使用线程安全的Singleton初始化代码停止执行代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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