首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Objective-C 多執行緒安全:@synchronized 保護 NSMutableArray 等非執行緒安全集合

     編輯:關於C語言

Objective-C 多執行緒安全:@synchronized 保護 NSMutableArray 等非執行緒安全集合

Cocoa 基礎集合類(NSMutableArray、NSMutableDictionary、NSMutableSet)非執行緒安全。多執行緒並發讀寫會導致資料競爭、索引越界崩潰、內容不一致。本文通過實戰代碼演示風險,並以 @synchronized 方案解決,附帶執行結果驗證。

一、 風險場景

  • 工作執行緒更新資料模型(如 addObject:)。
  • 主執行緒根據模型刷新 UI(如 count、objectAtIndex:)。
  • 無鎖保護時,NSMutableArray 內部結構損壞,導致 Crash 或資料錯誤。

二、 解決方案:@synchronized 互斥鎖

@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 屬性 ≠ 複合操作原子。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved