我们要保证静态变量的线程安全性。 我们在@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;
}
}
答案 0 :(得分:2)
tl; dr:这是非常安全的,只要字符串文字是唯一的即可。如果不是唯一的,则可能存在(良性)问题,但通常仅在发布模式下。不过,可能有一种更简单的方法可以实现这一点。
class MyModel(models.Model):
myfield = models.CharField(help_text='My Help Text')
块是使用运行时函数@synchronized
和objc_sync_enter
(source)实现的。这些功能是通过使用指针值作为键的全局(但在objc内部)锁的边表来实现的。在C-API级别上,您也可以锁定objc_sync_exit
或实际上任何指针值。对象是否处于活动状态甚至都没有关系,因为永远不会取消引用指针。但是,如果(void *)42
没有静态类型检查为@synchronized(obj)
类型(其中obj
是子类型,那么objc编译器将拒绝编译id
表达式)也许它保留了对象(我不确定),所以您应该只将其用于对象。
尽管有两个要点要考虑:
NSString *
是NULL指针(目标C中的obj
),则nil
和objc_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行之间存在竞争条件。(不想在此处进行详细说明。如果您需要更多信息,请问一个新问题。 )