MST
星途 面试题库

面试题:Objective-C中Block与GCD多线程同步问题

假设在Objective-C项目中使用Block结合GCD进行多线程编程,在多个任务需要按顺序执行且存在数据依赖的情况下,如何利用Block和GCD的特性保证线程安全与执行顺序,并分析可能出现的死锁场景及解决办法。
40.0万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

利用Block和GCD保证线程安全与执行顺序

  1. 使用串行队列
    • 创建一个串行队列,将依赖的任务依次添加到该队列中。这样,任务会按照添加的顺序依次执行。
    • 示例代码如下:
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];
        });
    });
});
  1. 利用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"];
});

可能出现的死锁场景及解决办法

  1. 死锁场景
    • 循环依赖:例如任务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等待第一个任务完成,而第一个任务等待第二个任务完成
    });
});
  1. 解决办法
    • 避免循环依赖:仔细分析任务之间的依赖关系,打破循环。可以通过重新设计任务逻辑,或者引入中间数据缓存来避免直接的循环依赖。
    • 避免同步调用死锁
      • 对于串行队列,尽量使用dispatch_async异步提交任务。如果必须使用同步调用,确保不会在当前队列中同步提交任务。
      • 例如,修改上述代码为:
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{
    dispatch_async(serialQueue, ^{
        // 不会发生死锁,任务异步执行
    });
});