objective-c - Objective-C 上局部静态变量的线程安全

标签 objective-c multithreading race-condition static-variables

因为以下代码是 Objective-C 上用于创建实例并确保它是线程安全的相当常见的模式。 但是,此线程安全基于一个重要条件,即编译器保证局部静态变量是线程安全的,这意味着静态 _sharedCache 指针将保证以线程安全的方式创建,但是我不能找出有关此的任何文档。有谁能给我提供更有把握的证据吗? (由于这里的人一直关注我一开始使用的可变集,所以我只是将其更改为 NSCache,这真的不是重点。我在这里谈论创建本地静态指针的线程安全 (不是这个指针指向的实例))

+ (NSCache*)sharedCache {
  static NSCache* _sharedCache = nil;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    _sharedCache = [[NSCache alloc] init];
  });
  return _sharedCache;
}

以防我没有把竞争条件描述清楚,考虑两个线程同时调用这个API,同时检查这个静态_sharedCache指针是否存在,如果不存在,他们将自己创建一个新的静态指针。那么这里可能是NSMutableSet的两个静态指针,即使dispatch_once保证了实例初始化只发生了一次,也就是只针对这两个静态指针中的一个,然后在dispatch_once block 之后,第一个调用者得到了满意的结果,但是第二个调用者只返回一个 nil 指针;

考虑这种情况,A和B是两个线程,同时输入这段代码,A查看这里的静态指针“_sharedSet”,发现这里没有这个指针,(指针是unsigned long的一个实例),所以创建一个并为其分配0,现在该指针存储在内存地址(0xAAAAAAAA)中,同时B做了相同的行为,在堆中创建了一个静态指针但内存地址为(0xBBBBBBBB),然后dispatch_once阻塞了两个线程直到它完成,所以它为创建的 NSSet 实例分配了一个新的指针值,并将该值分配给地址 0xAAAAAAAA,但是地址 0xBBBBBBBB 上的值仍然为 0,因为没有人更新它,所以线程 B 只返回一个 nil。所以基本上我的问题不是关于 dispatch_once 的怀疑,而是关于创建局部静态变量的线程安全性的双重问题,这是 C++ 上的一个有效问题,直到 C11 解决了它。不知道Clang是否也注意到了这个问题。

而且这个问题真的不是dispatch_once,而是local static variable,是具体的non-object variable,如这里所说的pointer,或者换个方式问,如果下面的代码在竞争线程中调用,这个"_intBuffer"是否会保证在堆中只有一个实例?

    +(int)sharedIntBuffer {
        static int _intBuffer = 0;
        return _intBuffer;
    }

最佳答案

您的代码更大的潜在问题是您正在共享一个可变集。正如 gnasher 解释的那样,您显示的使用 dispatch_once() 分配集合的代码很好,原因在那个答案中解释过。但是一旦完成,您就可能跨线程共享一个可变集。除非您采取措施同步对该集合的访问,否则您很容易遇到两个或多个线程同时更改该集合的问题。防止此类问题的最简单、最可靠的方法是将集合封装在一个类中,以确保以线程安全的方式访问集合,例如使用串行调度队列。

关于objective-c - Objective-C 上局部静态变量的线程安全,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25733091/

相关文章:

objective-c - 如何获取 NSTextField 的 stringValue 的字符串长度?

java - 唤醒 sleep 线程 - interrupt() 与 "splitting" sleep 进入多个 sleep

java - Android 线程阻塞 UI 除非使用同步

php - 防止在线酒店预订中的竞争条件

node.js - 使用 Node.js 防止竞争条件

ios - 除了 .storyboard(Outlet 引用)或 alloc int in view 之外的其他地方初始化的 UIlabels 是否加载?

ios - UIDocument openWithCompletionHandler : forces NSMetadataQuery to update

objective-c - 如何使用graph api、objective c将本地镜像发布到用户的 friend 墙上?

java - 在同一个对象上使用 Fork 和 Join

javascript - 使用 Ajax 的 Angular.js 中的 2 个 Controller 和本地存储值之间存在竞争条件问题