面试题答案
一键面试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的特性和方法,可以有效地保证在多线程环境下的线程安全,避免数据竞争。