ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Objective-C GCD并发编程与线程安全实践

Objective-C GCD并发编程与线程安全实践 1. Objective-C中的大中枢派发GCD核心概念在iOS/macOS开发中Grand Central DispatchGCD是管理并发操作的底层框架。它通过将线程管理的复杂性抽象为简单的队列模型让开发者能够更高效地利用多核处理器。GCD的核心思想是将任务blocks提交到队列queues由系统自动管理线程的创建和回收。GCD提供三种主要队列类型串行队列Serial Queue按FIFO顺序一次执行一个任务并发队列Concurrent Queue可以同时执行多个任务主队列Main Queue特殊的串行队列所有UI操作必须在此执行重要提示GCD不是简单的线程池而是更高级的任务调度系统。它会根据系统负载自动调整实际使用的线程数量。2. 线程安全的数据访问模式2.1 串行队列同步方案最基本的线程安全模式是使用串行队列配合同步/异步派发interface ThreadSafeModel : NSObject property (nonatomic, copy) NSString *sharedString; end implementation ThreadSafeModel { dispatch_queue_t _syncQueue; NSString *_sharedString; } - (instancetype)init { if (self [super init]) { _syncQueue dispatch_queue_create(com.example.syncQueue, DISPATCH_QUEUE_SERIAL); } return self; } - (NSString *)sharedString { __block NSString *localString; dispatch_sync(_syncQueue, ^{ localString _sharedString; }); return localString; } - (void)setSharedString:(NSString *)sharedString { dispatch_async(_syncQueue, ^{ _sharedString [sharedString copy]; }); } end这种模式的优缺点优点实现简单保证读写安全缺点所有操作串行执行读操作也需要等待2.2 并发队列与栅栏函数优化对于读多写少的场景可以使用并发队列配合栅栏函数barrier实现更高效的线程安全implementation ThreadSafeModel { dispatch_queue_t _concurrentQueue; NSString *_sharedString; } - (instancetype)init { if (self [super init]) { _concurrentQueue dispatch_queue_create(com.example.concurrentQueue, DISPATCH_QUEUE_CONCURRENT); } return self; } - (NSString *)sharedString { __block NSString *localString; dispatch_sync(_concurrentQueue, ^{ localString _sharedString; }); return localString; } - (void)setSharedString:(NSString *)sharedString { dispatch_barrier_async(_concurrentQueue, ^{ _sharedString [sharedString copy]; }); } end栅栏函数的特殊行为当栅栏block到达队列头部时会等待所有正在执行的block完成栅栏block执行时队列会暂时变为串行模式栅栏block完成后队列恢复并发执行实测性能对比iPhone 12 Pro10000次操作方案纯读(ms)纯写(ms)读写混合(ms)串行队列12.38.715.2并发栅栏4.19.26.83. GCD高级模式实践3.1 任务组Dispatch Group的工程应用Dispatch Group非常适合需要等待多个异步任务完成的场景。实际开发中的典型用例- (void)uploadMultipleImages:(NSArrayUIImage * *)images completion:(void(^)(BOOL success))completion { dispatch_group_t uploadGroup dispatch_group_create(); dispatch_queue_t uploadQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); NSMutableArray *results [NSMutableArray array]; for (UIImage *image in images) { dispatch_group_enter(uploadGroup); dispatch_async(uploadQueue, ^{ [self uploadImage:image completion:^(BOOL success) { synchronized(results) { [results addObject:(success)]; } dispatch_group_leave(uploadGroup); }]; }); } dispatch_group_notify(uploadGroup, dispatch_get_main_queue(), ^{ BOOL allSuccess YES; for (NSNumber *result in results) { if (!result.boolValue) { allSuccess NO; break; } } completion(allSuccess); }); }关键技巧必须在每个异步任务开始前调用dispatch_group_enter在每个任务完成回调中调用dispatch_group_leavedispatch_group_notify会在所有任务完成后自动调用使用synchronized保护共享数据的线程安全3.2 dispatch_apply的高效循环dispatch_apply是并行化循环的利器特别适合处理相互独立的迭代任务- (void)processLargeArray:(NSArray *)dataArray { dispatch_queue_t queue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 传统for循环 // for (int i 0; i dataArray.count; i) { // [self processItem:dataArray[i]]; // } // 并行化处理 dispatch_apply(dataArray.count, queue, ^(size_t i) { [self processItem:dataArray[i]]; }); NSLog(All items processed); // 注意dispatch_apply是同步的 }使用注意事项循环内的任务应该相互独立没有执行顺序依赖避免在循环block中修改共享状态系统会根据当前负载自动决定并行度整个函数是阻塞的会在所有迭代完成后才继续执行性能对比处理10000个元素方案执行时间(ms)CPU利用率普通for循环2450~25%dispatch_apply680~90%4. 常见陷阱与最佳实践4.1 避免dispatch_get_current_queue导致的死锁曾经常见的错误模式// 危险代码不要使用 - (NSString *)someString { __block NSString *localString; dispatch_block_t block ^{ localString _someString; }; if (dispatch_get_current_queue() _syncQueue) { block(); } else { dispatch_sync(_syncQueue, block); } return localString; }这种代码的问题在于dispatch_get_current_queue()在iOS 6已被废弃无法处理队列层级关系导致的间接死锁违反了队列封装的抽象原则正确的解决方案架构interface SafeModel : NSObject property (nonatomic, copy) NSString *safeString; end implementation SafeModel { dispatch_queue_t _syncQueue; NSString *_safeString; } - (instancetype)init { if (self [super init]) { _syncQueue dispatch_queue_create(com.example.safeQueue, DISPATCH_QUEUE_SERIAL); } return self; } // 方案1完全异步设计 - (void)getSafeStringWithCompletion:(void(^)(NSString *))completion { dispatch_async(_syncQueue, ^{ NSString *result _safeString; dispatch_async(dispatch_get_main_queue(), ^{ if (completion) completion(result); }); }); } // 方案2同步设计但严格限制使用场景 - (NSString *)safeString { __block NSString *result; dispatch_sync(_syncQueue, ^{ result _safeString; }); return result; } - (void)setSafeString:(NSString *)safeString { dispatch_async(_syncQueue, ^{ _safeString [safeString copy]; }); } end4.2 performSelector的替代方案performSelector系列方法存在诸多限制现代Objective-C代码应该优先使用直接方法调用最简单直接NSInvocation灵活但复杂Block调用最推荐的方式协议和消息转发面向对象设计对比示例// 不推荐的performSelector方式 SEL selector selector(doSomethingWith:object:); if ([self respondsToSelector:selector]) { [self performSelector:selector withObject:arg1 withObject:arg2]; } // 推荐的Block方式 typedef void (^ActionBlock)(id arg1, id arg2); property (nonatomic, copy) ActionBlock actionHandler; // 调用时 if (self.actionHandler) { self.actionHandler(arg1, arg2); }Block方案的优势完善的类型检查自动内存管理支持任意数量参数可以捕获上下文与GCD完美配合5. GCD在工程中的高级应用5.1 自定义队列层级体系通过dispatch_set_target_queue可以建立队列间的依赖关系// 创建三个串行队列 dispatch_queue_t queueA dispatch_queue_create(com.example.queueA, NULL); dispatch_queue_t queueB dispatch_queue_create(com.example.queueB, NULL); dispatch_queue_t queueC dispatch_queue_create(com.example.queueC, NULL); // 建立层级queueB - queueAqueueC - queueA dispatch_set_target_queue(queueB, queueA); dispatch_set_target_queue(queueC, queueA); // 此时 // 1. queueB和queueC中的任务实际上由queueA调度 // 2. 虽然queueA是串行队列但queueB和queueC的任务可以并行执行 // 3. 可以通过suspend/resume控制整个队列树典型应用场景限制并发任务总数建立任务优先级体系批量控制任务执行5.2 队列特定数据Queue-Specific DataGCD提供了存储队列关联数据的API可用于解决复杂的同步问题static void *kQueueKey kQueueKey; - (void)setupQueueSpecificData { dispatch_queue_t queueA dispatch_queue_create(com.example.queueA, NULL); dispatch_queue_t queueB dispatch_queue_create(com.example.queueB, NULL); // 设置队列特定值 dispatch_queue_set_specific(queueA, kQueueKey, (__bridge void *)queueA, NULL); // 检查当前队列 dispatch_async(queueB, ^{ NSString *value (__bridge NSString *)dispatch_get_specific(kQueueKey); if (value) { NSLog(Running on queueA or its target queue); } else { NSLog(Running on other queue); } }); }实际工程中的应用递归锁检测调试队列信息实现上下文感知的API避免过度同步5.3 延迟执行的最佳实践GCD提供了更强大的延迟执行机制// 基本延迟执行 dispatch_time_t delay dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)); dispatch_after(delay, dispatch_get_main_queue(), ^{ [self doSomething]; }); // 带取消功能的延迟执行 interface MyClass : NSObject property (nonatomic, strong) dispatch_block_t delayedBlock; end implementation MyClass - (void)scheduleDelayedTask { __weak typeof(self) weakSelf self; self.delayedBlock dispatch_block_create(0, ^{ [weakSelf doSomething]; }); dispatch_time_t delay dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)); dispatch_after(delay, dispatch_get_main_queue(), self.delayedBlock); } - (void)cancelDelayedTask { if (self.delayedBlock) { dispatch_block_cancel(self.delayedBlock); self.delayedBlock nil; } } end延迟执行的工程考量注意循环引用问题提供取消机制考虑任务执行时的对象状态避免堆积过多延迟任务6. 性能优化与调试技巧6.1 GCD性能调优队列创建开销每个dispatch_queue_t大约需要1KB内存Block捕获成本注意避免在block中捕获大型对象线程爆炸问题过多的并发任务会导致线程过多增加上下文切换开销优先级反转避免高优先级任务等待低优先级任务性能检查清单使用Instruments的Dispatch工具检查队列使用情况监控线程数量不应超过CPU核心数的2-4倍避免在热路径上频繁创建/销毁队列使用dispatch_benchmark测量关键代码段// 性能测量示例 size_t const count 1000; uint64_t t dispatch_benchmark(1000, ^{ autoreleasepool { for (size_t i 0; i count; i) { autoreleasepool { NSObject *obj [[NSObject alloc] init]; [NSArray arrayWithObject:obj]; } } } }); NSLog(Average runtime: %llu ns, t);6.2 常见问题排查死锁诊断使用po [NSThread callStackSymbols]查看所有线程堆栈检查队列依赖关系寻找同步派发到当前队列的情况线程过多检查是否过度使用全局并发队列考虑使用dispatch_apply限制并发度使用串行队列替代不必要的并发性能瓶颈检查是否在主队列执行耗时操作使用Time Profiler定位热点考虑使用dispatch_async分层处理内存问题注意block捕获循环及时释放不需要的队列避免在队列中累积太多未执行任务7. 现代Objective-C并发编程演进7.1 与OperationQueue的对比选择GCD与NSOperationQueue的对比特性GCDNSOperationQueue抽象层级较低接近系统层较高面向对象任务取消困难原生支持依赖管理无完善支持优先级有限支持精细控制KVO兼容无完全支持适用场景简单并发任务复杂任务依赖现代工程中的混合使用模式// 使用GCD处理底层并发 - (NSData *)processImageData:(NSData *)input { __block NSData *result; dispatch_sync(_imageProcessingQueue, ^{ result [self _heavyProcessing:input]; }); return result; } // 使用OperationQueue管理高级任务流 - (void)performImageProcessingPipeline { NSOperationQueue *pipelineQueue [[NSOperationQueue alloc] init]; pipelineQueue.maxConcurrentOperationCount 2; NSBlockOperation *downloadOp [NSBlockOperation blockOperationWithBlock:^{ [self downloadImage]; }]; NSBlockOperation *processOp [NSBlockOperation blockOperationWithBlock:^{ [self processImage]; }]; NSBlockOperation *uploadOp [NSBlockOperation blockOperationWithBlock:^{ [self uploadResult]; }]; // 建立依赖关系 [processOp addDependency:downloadOp]; [uploadOp addDependency:processOp]; [pipelineQueue addOperations:[downloadOp, processOp, uploadOp] waitUntilFinished:NO]; }7.2 向Swift并发体系的迁移Swift的async/await模型提供了更现代的并发编程方式但在Objective-C中仍可借鉴其思想使用Completion Handler模拟async/await模式typedef void (^AsyncResult)(id result, NSError *error); - (void)fetchDataAsync:(void(^)(NSData *data, NSError *error))completion { dispatch_async(_networkQueue, ^{ NSData *data [self _syncFetchData]; dispatch_async(dispatch_get_main_queue(), ^{ if (completion) completion(data, nil); }); }); } // 使用示例 [self fetchDataAsync:^(NSData *data, NSError *error) { if (data) { [self processData:data]; } }];构建Promise-like模式interface AsyncTask : NSObject (instancetype)run:(id(^)(void))block onQueue:(dispatch_queue_t)queue; - (void)then:(void(^)(id result))thenBlock; end // 使用示例 [[[AsyncTask run:^{ return [self fetchData]; } onQueue:_networkQueue] then:^(NSData *data) { [self processData:data]; }] then:^{ [self updateUI]; }];桥接Swift并发代码// Swift端 objc class DataProcessor: NSObject { objc static func processData(_ data: Data, completion: escaping (Data?, Error?) - Void) { Task { do { let result try await processDataAsync(data) completion(result, nil) } catch { completion(nil, error) } } } private static func processDataAsync(_ data: Data) async throws - Data { // 使用Swift并发处理 } }// Objective-C端 [DataProcessor processData:inputData completion:^(NSData *result, NSError *error) { if (result) { [self handleResult:result]; } }];在维护大型Objective-C项目时渐进式地采用这些现代模式可以使代码更易于维护同时为将来迁移到Swift做好准备。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进