Cocoa 基礎集合類(NSMutableArray、NSMutableDictionary、NSMutableSet)非執行緒安全。多執行緒並發讀寫會導致資料競爭、索引越界崩潰、內容不一致。本文通過實戰代碼演示風險,並以 @synchronized 方案解決,附帶執行結果驗證。
addObject:)。count、objectAtIndex:)。NSMutableArray 內部結構損壞,導致 Crash 或資料錯誤。@synchronized(obj) { ... } 以 obj 為鎖標記,確保同一時間僅一條執行緒進入臨界區。類似 Java synchronized。
#import <Foundation/Foundation.h>
#import <pthread.h>
@interface MyDevice : NSObject
@property (readonly) NSMutableArray *usbArray;
@property (readwrite, assign, atomic) NSInteger usbCount; // atomic 僅保證讀寫原子性,不保證 ++ 原子
@end
@implementation MyDevice {
NSMutableArray *_usbArray;
NSInteger _usbCount;
}
@synthesize usbArray = _usbArray;
@synthesize usbCount = _usbCount;
- (instancetype)init {
if (self = [super init]) {
_usbArray = [NSMutableArray new];
_usbCount = 0;
}
return self;
}
@end
static MyDevice *gDevice;
static int gThreadDone = 0;
#define THREAD_COUNT 100
#define LOOP_PER_THREAD 10000
void *threadFunc(void *arg) {
for (int i = 0; i < LOOP_PER_THREAD; i++) {
// 原始類型 ++ 非原子,需鎖保護
@synchronized(gDevice) {
gDevice.usbCount = gDevice.usbCount + 1;
}
// 集合操作必須加鎖
@synchronized(gDevice.usbArray) {
[gDevice.usbArray addObject:@(i)];
}
}
__sync_fetch_and_add(&gThreadDone, 1);
return NULL;
}
int main() {
gDevice = [MyDevice new];
pthread_t threads[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++) {
pthread_create(&threads[i], NULL, threadFunc, NULL);
}
for (int i = 0; i < THREAD_COUNT; i++) {
pthread_join(threads[i], NULL);
}
NSLog(@"usbArray count: %lu", (unsigned long)gDevice.usbArray.count);
NSLog(@"usbCount: %ld", (long)gDevice.usbCount);
// 斷言驗證
NSAssert(gDevice.usbCount <= gDevice.usbArray.count, @"Count mismatch");
NSAssert(gDevice.usbArray.count == THREAD_COUNT * LOOP_PER_THREAD, @"Array count mismatch");
return 0;
}
usbArray count: 1000000
usbCount: 1000000
加鎖後,usbCount 與 usbArray.count 完全一致(100 萬),無資料遺失、無崩潰。
對比無鎖版本(註釋 @synchronized):usbCount 會小於 100 萬(因 ++ 非原子),usbArray 甚至崩潰。
@property atomic 僅保證 getter/setter 單次讀寫原子,不保證 count++ 這類 讀-改-寫 操作原子。dispatch_queue_t 串列隊列、NSLock/pthread_mutex_t、或無鎖資料結構。dispatch_barrier_async 於併發隊列可提升讀性能。非執行緒安全集合 + 多執行緒 = 必須加鎖。@synchronized 是最簡單、可讀性最高的鎖方案,適用於大多數業務場景。記住:atomic 屬性 ≠ 複合操作原子。