Grand Central Dispatch(GCD)在iOS中的常见运用场景
GCD是Apple提供的多线程编程技术,旨在提供高效、轻量级的方式来执行并发任务。GCD使得管理线程变得简单且提高了应用程序的性能。以下是GCD在iOS中的一些常见运用场景,并详细介绍其底层原理。
1. 异步任务处理
场景:网络请求
使用GCD进行异步网络请求,使UI不被阻塞。
示例代码:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://example.com"]];
dispatch_async(dispatch_get_main_queue(), ^{
// 更新UI
self.imageView.image = [UIImage imageWithData:data];
});
});
原理解析:
dispatch_async
: 提交任务到一个队列,且不阻塞当前线程。dispatch_get_global_queue
: 获取全局并发队列。dispatch_get_main_queue
: 获取主队列,以在主线程上更新UI。
GCD的底层通过创建和管理轻量级的任务单元(blocks),并将这些任务提交到由系统管理的低层次队列(dispatch queues)。系统确保这些任务在适当的线程上执行,从而优化性能和资源使用。
2. 并发执行任务
场景:批量图片下载
示例代码:
dispatch_group_t group = dispatch_group_create();
for (NSString *urlString in urlArray) {
dispatch_group_enter(group);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlString]];
if (data) {
UIImage *image = [UIImage imageWithData:data];
// 处理图片
}
dispatch_group_leave(group);
});
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 所有任务完成后回到主线程
[self updateUI];
});
原理解析:
dispatch_group_create
: 创建调度组。dispatch_group_enter
: 进入调度组。dispatch_group_leave
: 离开调度组。dispatch_group_notify
: 所有调度组内任务完成后执行特定任务。
GCD的调度组允许将多个任务组合在一起,追踪这些任务的完成情况。当所有任务完成后,可以进行统一的后续操作。
3. 任务的延时执行
场景:避免频繁的UI更新(防抖)
示例代码:
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC);
dispatch_after(delay, dispatch_get_main_queue(), ^{
// 延时1秒执行的任务
[self performUIUpdate];
});
原理解析:
dispatch_time
: 计算未来的时间点。dispatch_after
: 在指定延迟后执行任务。
GCD处理定时任务时,底层会创建一个计时器,并在计时结束后将任务提交到相应的队列中执行。
4. 一次性执行
场景:单例模式
示例代码:
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
static MyClass *instance = nil;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
原理解析:
dispatch_once
: 确保代码块只执行一次。
dispatch_once
使用底层的 atomic operations 确保线程安全,即使在并发访问的情况下也能保证代码块只被执行一次。
5. 线程同步
场景:访问共享资源
示例代码:
@property (nonatomic, strong) dispatch_queue_t syncQueue;
- (instancetype)init {
self = [super init];
if (self) {
_syncQueue = dispatch_queue_create("com.example.syncQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)updateSharedResource {
dispatch_sync(self.syncQueue, ^{
// 访问和修改共享资源
});
}
原理解析:
dispatch_queue_create
: 创建一个串行队列。dispatch_sync
: 同步执行任务。
使用同步队列确保对共享资源的访问和修改是线程安全的。GCD通过串行化执行保证数据库访问、文件读写等不发生竞争情况。
6. 定期任务
场景:游戏中的刷新逻辑或实时更新
示例代码:
@property (nonatomic, strong) dispatch_source_t timer;
- (void)startTimer {
dispatch_queue_t queue = dispatch_get_main_queue();
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(self.timer, dispatch_walltime(NULL, 0), 1.0 * NSEC_PER_SEC, 0);
dispatch_source_set_event_handler(self.timer, ^{
// 定期运行的任务
[self updateGameState];
});
dispatch_resume(self.timer);
}
- (void)stopTimer {
dispatch_source_cancel(self.timer);
self.timer = NULL;
}
原理解析:
dispatch_source_create
: 创建调度源,常用于计时器、文件描述符、信号等。dispatch_source_set_timer
: 设置计时器的开始时间和重复间隔。dispatch_source_set_event_handler
: 设置事件处理程序。
GCD的调度源基于"run loop"机制,可以高效地处理定期任务,同时通过驻留的内核对象来管理计时器等资源。
7. 信号量控制
场景:控制并发数量
示例代码:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(3); // 最大并发数为3
for (NSString *urlString in urlArray) {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlString]];
if (data) {
UIImage *image = [UIImage imageWithData:data];
// 处理图片
}
dispatch_semaphore_signal(semaphore);
});
}
原理解析:
dispatch_semaphore_create
: 创建信号量。dispatch_semaphore_wait
: 等待信号。dispatch_semaphore_signal
: 发送信号。
信号量通过计数机制控制并发线程的数量,GCD底层使用信号量的原子操作确保线程安全。
8. Barrier Block
场景:多读单写
示例代码:
dispatch_queue_t queue = dispatch_queue_create("com.example.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
// 读操作
[self readData];
});
dispatch_async(queue, ^{
// 读操作
[self readData];
});
dispatch_barrier_async(queue, ^{
// 写操作
[self writeData];
});
dispatch_async(queue, ^{
// 读操作
[self readData];
});
原理解析:
DISPATCH_QUEUE_CONCURRENT
: 创建并发队列。dispatch_barrier_async
: 提交栅栏任务,确保它之前和之后的并发任务都完成,将其与其他任务隔离开来。
Barrier Block允许在并发队列中实现类似于数据库的多读单写机制,从而确保资源的一致性。
9. 主队列死锁问题
场景:防止死锁
示例代码:
// 在主队列上同步提交任务会导致死锁
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"This will never be printed");
});
原理解析:
- 主队列是串行队列,如果在主线程上同步提交任务,会导致死锁,因为主线程自身正在等待提交的任务完成。
避免在主线程上同步提交任务,否则会导致严重的阻塞问题。GCD的底层实现通过检查pthread_main_np
来判定当前是否在主线程上进行阻塞操作,如是则抛出异常或警告。
总结
GCD通过诸如dispatch queues、dispatch groups、dispatch sources和semaphores等基础设施,提供了一种高效、优雅的并发编程方式。其底层主要基于任务调度器、线程池和内核对象(如计时器、信号量)等组成。通过合理使用这些组件,开发者可以显著提高应用程序的响应速度和资源利用率。
标签:group,GCD,self,iOS,dispatch,queue,任务,112 From: https://www.cnblogs.com/chglog/p/18307064