静态变量可以用作@synchronized 参数吗?
Can a static variable used as @synchronized parameter?
我们想保证静态变量的线程安全。
我们在@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;
}
}
tl;dr: 只要字符串文字是唯一的,这是绝对安全的。如果它不是唯一的,则可能存在(良性)问题,但通常仅在发布模式下存在。不过,可能有更简单的方法来实现这一点。
@synchronized
块是使用运行时函数 objc_sync_enter
和 objc_sync_exit
(source) 实现的。这些函数是使用全局(但 objc 内部)侧 table 锁实现的,该锁由指针值键入。在 C-API 级别,您还可以锁定 (void *)42
,或者实际上是任何指针值。对象是否存在甚至都没有关系,因为指针永远不会被取消引用。但是,如果 obj
没有静态类型检查为 id
类型(其中 NSString *
是一个子类型,所以没关系),objc 编译器拒绝编译 @synchronized(obj)
表达式也许它保留了对象(我不确定),所以你应该只将它与对象一起使用。
不过有两个关键点需要考虑:
- 如果您同步的
obj
是 NULL 指针(Objective C 中的 nil
),则 objc_sync_enter
和 objc_sync_exit
不是- ops,这会导致在完全没有锁定的情况下执行块的不良情况。
- 如果您对不同的
@synchronized
块使用相同的字符串值,编译器可能足够聪明,可以将它们映射到相同的指针地址。也许编译器现在不这样做,但它是 Apple 将来可能引入的完全有效的优化。因此,您应该确保使用唯一的名称。如果发生这种情况,两个不同的 @synchronized
块可能会在程序员想要使用不同锁的地方意外地使用同一个锁。对了,也可以用[NSObject new]
作为锁对象。
在 class 对象 ([MyClass class]
) 上同步也是非常安全的。
现在介绍更简单的方法。如果你只有一个 BOOL 变量,你想成为原子变量,你可以使用无锁编程:
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
}
}
+save
方法根本不是线程安全的,因为第 22 行和第 23 行之间存在竞争条件。(不想在这里详细说明.. 如果需要,请提出一个新问题更多信息。)
我们想保证静态变量的线程安全。 我们在@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;
}
}
tl;dr: 只要字符串文字是唯一的,这是绝对安全的。如果它不是唯一的,则可能存在(良性)问题,但通常仅在发布模式下存在。不过,可能有更简单的方法来实现这一点。
@synchronized
块是使用运行时函数 objc_sync_enter
和 objc_sync_exit
(source) 实现的。这些函数是使用全局(但 objc 内部)侧 table 锁实现的,该锁由指针值键入。在 C-API 级别,您还可以锁定 (void *)42
,或者实际上是任何指针值。对象是否存在甚至都没有关系,因为指针永远不会被取消引用。但是,如果 obj
没有静态类型检查为 id
类型(其中 NSString *
是一个子类型,所以没关系),objc 编译器拒绝编译 @synchronized(obj)
表达式也许它保留了对象(我不确定),所以你应该只将它与对象一起使用。
不过有两个关键点需要考虑:
- 如果您同步的
obj
是 NULL 指针(Objective C 中的nil
),则objc_sync_enter
和objc_sync_exit
不是- ops,这会导致在完全没有锁定的情况下执行块的不良情况。 - 如果您对不同的
@synchronized
块使用相同的字符串值,编译器可能足够聪明,可以将它们映射到相同的指针地址。也许编译器现在不这样做,但它是 Apple 将来可能引入的完全有效的优化。因此,您应该确保使用唯一的名称。如果发生这种情况,两个不同的@synchronized
块可能会在程序员想要使用不同锁的地方意外地使用同一个锁。对了,也可以用[NSObject new]
作为锁对象。
在 class 对象 ([MyClass class]
) 上同步也是非常安全的。
现在介绍更简单的方法。如果你只有一个 BOOL 变量,你想成为原子变量,你可以使用无锁编程:
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
}
}
+save
方法根本不是线程安全的,因为第 22 行和第 23 行之间存在竞争条件。(不想在这里详细说明.. 如果需要,请提出一个新问题更多信息。)