Objective c 静态变量可以用作@synchronized参数吗?
我们想保证静态变量的线程安全性。 我们在@synchronized指令中使用了另一个静态变量作为对象。像这样:Objective c 静态变量可以用作@synchronized参数吗?,objective-c,multithreading,static,thread-safety,synchronized,Objective C,Multithreading,Static,Thread Safety,Synchronized,我们想保证静态变量的线程安全性。 我们在@synchronized指令中使用了另一个静态变量作为对象。像这样: static NSString *_saveInProgressLock = @"SaveInProgressLock"; static BOOL _saveInProgress; + (BOOL)saveInProgress { @synchronized(_saveInProgressLock) { return _saveInProgress;
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\u sync\u enter
和objc\u sync\u exit
()实现。这些函数是使用全局(但objc内部)锁边表实现的,锁边表由指针值键控。在C-API级别上,您还可以锁定(void*)42
,或者实际上锁定任何指针值。对象是否活动都无关紧要,因为指针从未被解除引用。但是,如果obj
没有对id
类型进行静态类型检查(其中NSString*
是一个子类型,所以它是可以的),那么objc编译器拒绝编译@synchronized(obj)
表达式,并且可能它保留了对象(我不确定),因此您应该只对对象使用它
有两个关键点需要考虑:
- 如果进行同步的
是空指针(obj
,在目标C中),则nil
和objc\u sync\u enter
都不是操作,这会导致执行块时绝对没有锁定的不良情况objc\u sync\u exit
- 如果对不同的
块使用相同的字符串值,编译器可能足够聪明,可以将它们映射到相同的指针地址。也许编译器现在没有这样做,但这是一个完美有效的优化,苹果可能会在未来引入。因此,您应该确保使用唯一的名称。如果发生这种情况,两个不同的@synchronized
块可能会意外地使用同一个锁,而程序员希望使用不同的锁。顺便说一下,您还可以将@synchronized
用作锁对象[NSObject new]
[MyClass]
)上同步也是完全安全的
现在来看看更简单的方法。如果您只有一个想要成为原子的BOOL变量,则可以使用无锁编程:
static BOOL _saveInProgress;
+ (BOOL)saveInProgress {
__sync_synchronize();
return _saveInProgress;
}
+ (void)setSaveInProgress:(BOOL)save {
_saveInProgress = save;
__sync_synchronize();
}
这具有更好的性能,并且是线程安全的<代码>\uuu sync\u 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行之间存在争用条件。(不想在此详述……如果需要更多信息,只需问一个新问题即可。)tl;dr:只要字符串文本是唯一的,这是完全安全的。如果不是唯一的,则可能存在(良性)问题,但通常仅在发布模式下。不过,可能有一种更简单的方法来实现这一点
@synchronized
块使用运行时函数objc\u sync\u enter
和objc\u sync\u exit
()实现。这些函数是使用全局(但objc内部)锁边表实现的,锁边表由指针值键控。在C-API级别上,您还可以锁定(void*)42
,或者实际上锁定任何指针值。对象是否活动都无关紧要,因为指针从未被解除引用。但是,如果obj
没有对id
类型进行静态类型检查(其中NSString*
是一个子类型,所以它是可以的),那么objc编译器拒绝编译@synchronized(obj)
表达式,并且可能它保留了对象(我不确定),因此您应该只对对象使用它
有两个关键点需要考虑:
- 如果进行同步的
是空指针(obj
,在目标C中),则nil
和objc\u sync\u enter
都不是操作,这会导致执行块时绝对没有锁定的不良情况objc\u sync\u exit
- 如果对不同的
块使用相同的字符串值,编译器可能足够聪明,可以将它们映射到相同的指针地址。也许编译器现在没有这样做,但这是一个完美有效的优化,苹果可能会在未来引入。因此,您应该确保使用唯一的名称。如果发生这种情况,两个不同的@synchronized
块可能会意外地使用同一个锁,而程序员希望使用不同的锁。顺便说一下,您还可以将@synchronized
用作锁对象[NSObject new]
[MyClass]
)上同步也是完全安全的
现在来看看更简单的方法。如果您只有一个想要成为原子的BOOL变量,则可以使用无锁编程:
static BOOL _saveInProgress;
+ (BOOL)saveInProgress {
__sync_synchronize();
return _saveInProgress;
}
+ (void)setSaveInProgress:(BOOL)save {
_saveInProgress = save;
__sync_synchronize();
}
这具有更好的性能,并且是线程安全的<代码>\uuu sync\u 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行之间存在争用条件。(不想在这里详细说明。如果需要更多信息,只需问一个新问题即可。)谢谢您的详细解释!谢谢你的详细解释!