【问题标题】:Thread-Safe Singleton Synchronized() in Objective-CObjective-C 中的线程安全单例 Synchronized()
【发布时间】:2015-03-30 07:02:09
【问题描述】:

在我们的应用程序中,我们在多个位置使用了单例,最近我查看并在所有单例方法中添加了@synchronized 命令,以确保它们是线程安全的。我的问题是调用这个有什么区别:

+ (RLReceiver *) getReceiver
{
    static RLReceiver *receiverCache;
    @synchronized(receiverCache)
    {
        if (!receiverCache )
            receiverCache = [[RLReceiver alloc] init];
        return receiverCache;
    }
}

在这种情况下,我同步了 RLReceiver 类的静态实例,但我也看到了(而且编译器也允许这样做):

+ (RLReceiver *) getReceiver
{
    static RLReceiver *receiverCache;
    @synchronized(self)
    {
        if (!receiverCache )
            receiverCache = [[RLReceiver alloc] init];
        return receiverCache;
    }
}

同步在self 上。这让我有点困惑,因为这个方法是一个类方法,在这个方法的这个范围内甚至不应该有self。任何人都可以阐明在这种情况下静态变量和 self 之间的区别是什么,以及在类方法中甚至会有 self 吗?

【问题讨论】:

  • 第一个是第一次同步。第二个在对象的类对象上同步。 (不是 100% 确定这样做是合法的。)
  • 使用dispatch_once 的方案之一可能更明智。

标签: objective-c thread-safety singleton synchronized


【解决方案1】:

根据Apple Doc:“传递给@synchronized指令的对象是用于区分受保护块的唯一标识符。如果您在两个不同的线程中执行上述方法,则为每个线程上的anObj参数传递不同的对象, 每个线程都会获取它的锁并继续处理而不会被另一个阻塞。但是,如果在两种情况下传递相同的对象,一个线程将首先获取锁,另一个线程将阻塞,直到第一个线程完成临界区。”

//苹果示例:

- (void)myMethod:(id)anObj
{
@synchronized(anObj)
{
    // Everything between the braces is protected by the @synchronized directive.
}
}

就您而言,Hot Licks 表示问题在于首次发布。您的对象在第一次启动时不存在,并且同步无法正常工作。如果你尝试:

[1]

 +(RLReceiver *) getReceiver
{
static RLReceiver *receiverCache;

@synchronized(receiverCache)
{
    if (!receiverCache ) {
        receiverCache = [[RLReceiver alloc] init];

    }
    for (int i=0; i<100; i++) {
        NSLog(@"Numbers in order i %i",i);
    }

    return receiverCache;
}
} 

和[2]

+ (RLReceiver *) getReceiver
{
static RLReceiver *receiverCache;

@synchronized(self)
{
    if (!receiverCache ) {
        receiverCache = [[RLReceiver alloc] init];

    }
    for (int i=0; i<100; i++) {
        NSLog(@"Numbers in order i %i",i);
    }

    return receiverCache;
}
}

从另一个对象第一次调用类似的东西:

    dispatch_async(dispatch_queue_create("OtherQueue", 0), ^{
RLReceiver *rece= [RLReceiver getReceiver];
});
RLReceiver *receSorF = [RLReceiver getReceiver];

您可以看到在 [1] 的情况下,数字是如何混合的并且同步不起作用。在 [2] 的情况下,一个计数等待另一个。

我们可以在同步现有对象中的代码时使用该对象,其他情况下为类名。

感谢@HotLicks。

【讨论】:

  • 好答案。我认为我的困惑来自于在类方法内部self == [RLReceiver class] 这对我来说很奇怪,类方法内部的 self 指的是类对象,而在实例方法中 self 指的是类的实例。看来您根本不应该在类方法中使用 self 。
  • 呃,第一次调用第一个版本时同步的是什么? receiverCache 将是 nil
  • @HotLicks 你是对的。第一次将为空。但它在火之前是空的:receiverCache = [[RLReceiver alloc] init];并且它将被null同步。使用自我要好得多。
  • 鉴于您真正需要同步的唯一时间是第一次通过,我会说这不是一件小事。 “好多了”是轻描淡写的说法。
  • @HotLicks 谢谢,在这种情况下 self 它是你说的唯一选择。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-10
  • 2021-06-07
  • 2013-03-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多