MST
星途 面试题库

面试题:Objective-C中GCD的同步与异步执行及线程安全

假设你有一个复杂的业务逻辑,需要在多线程环境下处理数据,既要保证部分操作的顺序执行,又要提高整体效率。请使用Objective-C的GCD实现该逻辑,并说明如何通过GCD保证线程安全,避免数据竞争。
11.7万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

1. 使用GCD实现多线程下有顺序要求的复杂业务逻辑

假设我们有一系列操作,其中部分操作需要顺序执行,部分操作可以并行执行。以下是一个简单示例:

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 创建一个串行队列,用于顺序执行的任务
        dispatch_queue_t serialQueue = dispatch_queue_create("com.example.serialQueue", DISPATCH_QUEUE_SERIAL);
        // 创建一个并发队列,用于并行执行的任务
        dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);

        // 第一个顺序执行的任务
        dispatch_async(serialQueue, ^{
            NSLog(@"任务1开始执行");
            // 模拟一些工作
            [NSThread sleepForTimeInterval:1];
            NSLog(@"任务1执行完毕");
        });

        // 第二个顺序执行的任务,依赖第一个任务完成
        dispatch_async(serialQueue, ^{
            NSLog(@"任务2开始执行");
            // 模拟一些工作
            [NSThread sleepForTimeInterval:1];
            NSLog(@"任务2执行完毕");
        });

        // 并行执行的任务
        dispatch_async(concurrentQueue, ^{
            NSLog(@"并行任务1开始执行");
            // 模拟一些工作
            [NSThread sleepForTimeInterval:1];
            NSLog(@"并行任务1执行完毕");
        });

        dispatch_async(concurrentQueue, ^{
            NSLog(@"并行任务2开始执行");
            // 模拟一些工作
            [NSThread sleepForTimeInterval:1];
            NSLog(@"并行任务2执行完毕");
        });

        // 等待所有任务完成(仅为示例,实际应用中可能不需要这样等待主线程)
        dispatch_group_t group = dispatch_group_create();
        dispatch_group_enter(group);
        dispatch_group_async(group, serialQueue, ^{
            NSLog(@"最后一个顺序任务开始执行");
            // 模拟一些工作
            [NSThread sleepForTimeInterval:1];
            NSLog(@"最后一个顺序任务执行完毕");
            dispatch_group_leave(group);
        });
        dispatch_group_enter(group);
        dispatch_group_async(group, concurrentQueue, ^{
            NSLog(@"最后一个并行任务开始执行");
            // 模拟一些工作
            [NSThread sleepForTimeInterval:1];
            NSLog(@"最后一个并行任务执行完毕");
            dispatch_group_leave(group);
        });
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

    }
    return 0;
}

2. 通过GCD保证线程安全,避免数据竞争

  • 使用串行队列:将对共享资源的读写操作放在同一个串行队列中执行。因为串行队列每次只允许一个任务执行,所以不会出现多个任务同时访问和修改共享资源的情况。例如:
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.resourceQueue", DISPATCH_QUEUE_SERIAL);
__block int sharedVariable = 0;
dispatch_async(serialQueue, ^{
    sharedVariable = sharedVariable + 1;
});
dispatch_async(serialQueue, ^{
    NSLog(@"共享变量的值: %d", sharedVariable);
});
  • 使用dispatch_barrier_async:如果有一个并发队列,并且需要对共享资源进行读写操作。对于读操作,可以并发执行,但写操作需要独占访问。这时可以使用dispatch_barrier_async。示例如下:
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
__block NSMutableArray *sharedArray = [NSMutableArray array];

// 读操作
dispatch_async(concurrentQueue, ^{
    NSLog(@"读取数组元素: %@", sharedArray);
});

// 写操作
dispatch_barrier_async(concurrentQueue, ^{
    [sharedArray addObject:@"新元素"];
});

// 另一个读操作
dispatch_async(concurrentQueue, ^{
    NSLog(@"读取数组元素: %@", sharedArray);
});
  • 使用dispatch_semaphore:信号量可以用来控制对共享资源的访问数量。例如,创建一个值为1的信号量,在访问共享资源前等待信号量,访问完成后发送信号量,这样就可以保证同一时间只有一个任务可以访问共享资源。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
__block int sharedValue = 0;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    sharedValue = sharedValue + 1;
    dispatch_semaphore_signal(semaphore);
});

通过以上这些GCD的特性和方法,可以有效地保证在多线程环境下的线程安全,避免数据竞争。