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

问题描述 投票:0回答:1

我们希望保证静态变量的线程安全性。我们在@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;
    }
}
objective-c multithreading static thread-safety synchronized
1个回答
2
投票

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


@synchronized块使用运行时函数objc_sync_enterobjc_sync_exitsource)实现。这些函数是使用由指针值键入的全局(但是objc-internal)的锁边表来实现的。在C-API级别,您还可以锁定(void *)42,或实际上任何指针值。即使对象处于活动状态也无关紧要,因为指针永远不会被解除引用。然而,objc编译器拒绝编译@synchronized(obj)表达式,如果obj没有静态类型检查id类型(其中NSString *是一个子类型,所以它没关系)并且可能它保留了对象(我不确定),所以你应该只与对象一起使用它。

但有两个关键点需要考虑:

  • 如果你同步的obj是NULL指针(目标C中的nil),那么objc_sync_enterobjc_sync_exit都是no-ops,这导致了在完全没有锁定的情况下执行块的不良情况。
  • 如果对不同的@synchronized块使用相同的字符串值,则编译器可能足够智能以将它们映射到相同的指针地址。也许编译器现在不会这样做,但它是Apple可能在未来引入的完全有效的优化。所以你应该确保使用唯一的名称。如果发生这种情况,两个不同的@synchronized块可能会意外地使用相同的锁,程序员想要使用不同的锁。顺便说一下,你也可以使用[NSObject new]作为锁定对象。

在类对象([MyClass class])上同步是非常安全的,也可以。


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

static BOOL _saveInProgress;

+ (BOOL)saveInProgress {
    __sync_synchronize();
    return _saveInProgress;
}

+ (void)setSaveInProgress:(BOOL)save {
    _saveInProgress = save;
    __sync_synchronize();
}

这有更好的性能,就像线程安全一样。 qazxsw poi是一个记忆障碍。


但请注意,两种解决方案的安全性取决于您如何使用它们。如果你有一个看起来像这样的保存方法:

__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行之间存在竞争条件。(不要在这里详细说明..如果您需要更多信息,只需提出一个新问题。)

© www.soinside.com 2019 - 2024. All rights reserved.