利用Block和GCD保证线程安全与执行顺序
- 使用串行队列:
- 创建一个串行队列,将依赖的任务依次添加到该队列中。这样,任务会按照添加的顺序依次执行。
- 示例代码如下:
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{
// 第一个任务,例如获取数据
NSData *data = [self fetchData];
dispatch_async(serialQueue, ^{
// 第二个任务,依赖第一个任务的数据,例如解析数据
NSDictionary *parsedData = [self parseData:data];
dispatch_async(serialQueue, ^{
// 第三个任务,依赖第二个任务的数据,例如展示数据
[self displayData:parsedData];
});
});
});
- 利用Barrier任务(如果有读写操作):
- 如果任务中有对共享资源的读写操作,为了保证线程安全,可以使用
dispatch_barrier_async
。写操作使用dispatch_barrier_async
添加到队列,读操作使用dispatch_async
添加到队列。
- 示例代码如下:
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
// 读操作
dispatch_async(concurrentQueue, ^{
id sharedResource = [self readSharedResource];
});
// 写操作
dispatch_barrier_async(concurrentQueue, ^{
[self writeSharedResource:@"new value"];
});
可能出现的死锁场景及解决办法
- 死锁场景:
- 循环依赖:例如任务A依赖任务B的结果,任务B又依赖任务A的结果,形成循环依赖。
- 同步调用死锁:在串行队列中,使用
dispatch_sync
同步提交任务到当前队列。例如:
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue, ^{
dispatch_sync(serialQueue, ^{
// 这里会发生死锁,因为第二个dispatch_sync等待第一个任务完成,而第一个任务等待第二个任务完成
});
});
- 解决办法:
- 避免循环依赖:仔细分析任务之间的依赖关系,打破循环。可以通过重新设计任务逻辑,或者引入中间数据缓存来避免直接的循环依赖。
- 避免同步调用死锁:
- 对于串行队列,尽量使用
dispatch_async
异步提交任务。如果必须使用同步调用,确保不会在当前队列中同步提交任务。
- 例如,修改上述代码为:
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{
dispatch_async(serialQueue, ^{
// 不会发生死锁,任务异步执行
});
});