Objective-C中的线程安全Singleton Synchronized()

时间:2015-01-30 18:09:59

标签: objective-c thread-safety singleton synchronized

在我们的应用程序中,我们在多个位置使用单例,最近我通过并向所有单例方法添加了@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之间的区别是什么,在这种情况下,甚至在类方法中是否会有自我?

1 个答案:

答案 0 :(得分:0)

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

// Apple示例:

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

在你的情况下,Hot Licks如何说问题是第一次发布。您的对象在第一次启动时不存在,并且synchronized无法正常工作。如果您尝试:

[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。