可以将静态变量用作@synchronized参数吗?

时间:2018-08-31 08:44:28

标签: objective-c multithreading static thread-safety synchronized

我们要保证静态变量的线程安全性。 我们在@synchronized指令中使用了另一个静态变量作为对象。像这样:

static NSString *_saveInProgressLock = @"SaveInProgressLock";
static BOOL _saveInProgress;

+ (BOOL)saveInProgress {
    @synchronized(_saveInProgressLock) {
        return _saveInProgress;
    }
}

+ (void)setSaveInProgress:(BOOL)save {
    @synchronized(_saveInProgressLock) {
        _saveInProgress = save;
    }
}

我们正在商店中当前存在应用程序中的问题,可以通过阻止_saveInProgress变量设置为NO来重现这些问题。 您认为上述代码有任何问题吗?

它与此有何不同?

static BOOL _saveInProgress;

+ (BOOL)saveInProgress {
    @synchronized([MyClass class]) {
        return _saveInProgress;
    }
}

+ (void)setSaveInProgress:(BOOL)save {
    @synchronized([MyClass class]) {
        _saveInProgress = save;
    }
}

1 个答案:

答案 0 :(得分:2)

tl; dr:这是非常安全的,只要字符串文字是唯一的即可。如果不是唯一的,则可能存在(良性)问题,但通常仅在发布模式下。不过,可能有一种更简单的方法可以实现这一点。


class MyModel(models.Model): myfield = models.CharField(help_text='My Help Text')块是使用运行时函数@synchronizedobjc_sync_entersource)实现的。这些功能是通过使用指针值作为键的全局(但在objc内部)锁的边表来实现的。在C-API级别上,您也可以锁定objc_sync_exit或实际上任何指针值。对象是否处于活动状态甚至都没有关系,因为永远不会取消引用指针。但是,如果(void *)42没有静态类型检查为@synchronized(obj)类型(其中obj是子类型,那么objc编译器将拒绝编译id表达式)也许它保留了对象(我不确定),所以您应该只将其用于对象。

尽管有两个要点要考虑:

  • 如果您要同步的NSString *是NULL指针(目标C中的obj),则nilobjc_sync_enter是空操作,这导致到在没有锁定的情况下执行块的不良情况。
  • 如果对不同的objc_sync_exit块使用相同的字符串值,则编译器可能很聪明,可以将它们映射到相同的指针地址。也许编译器现在不执行此操作,但这是Apple将来可能会引入的完全有效的优化。因此,您应该确保使用唯一的名称。如果发生这种情况,程序员可能要使用不同的锁,而两个不同的@synchronized块可能会意外地使用同一锁。顺便说一句,您也可以将@synchronized用作锁定对象。

在类对象([NSObject new])上同步也是非常安全且可以的。


现在使用更简单的方法。如果只有一个想要成为原子的BOOL变量,则可以使用无锁编程:

[MyClass class]

这具有更好的性能,并且与线程安全一样。 static BOOL _saveInProgress; + (BOOL)saveInProgress { __sync_synchronize(); return _saveInProgress; } + (void)setSaveInProgress:(BOOL)save { _saveInProgress = save; __sync_synchronize(); } 是记忆障碍。


但是请注意,两种解决方案的安全性都取决于您如何使用它们。如果某个地方的保存方法如下所示:

__sync_synchronize()

+ (void)save { // line 21 if(![self saveInProgress]) { // line 22 [self setSaveInProgress:YES]; // line 23 // ... do stuff ... [self setSaveInProgress:NO]; // line 40 } } 方法根本不是线程安全的,因为在第22行和第23行之间存在竞争条件。(不想在此处进行详细说明。如果您需要更多信息,请问一个新问题。 )