iOS 多线程——GCD(二)
2023-09-05 06:11:25
前言
在上一篇文章中,我们主要探索了 GCD 的主队列及串行队列与并发队列在源码上的区分,以及同步函数的调用时机。本节我们将主要探索同步函数与异步函数的区别,包括任务回调是否具有同步性、异步性,以及深入分析同步函数的调用时机。
同步函数与异步函数的区别
同步函数
同步函数是指在调用函数时,当前线程会等待函数执行完毕后才继续执行。也就是说,在同步函数执行期间,当前线程会被阻塞,无法执行其他任务。
在 GCD 中,同步函数的典型代表是 dispatch_sync() 函数。该函数接收两个参数,第一个参数是队列,第二个参数是任务。任务是一个 block,其中包含了需要执行的代码。当调用 dispatch_sync() 函数时,当前线程会等待任务执行完毕后才继续执行。
异步函数
异步函数是指在调用函数时,当前线程不会等待函数执行完毕,而是继续执行后续代码。也就是说,在异步函数执行期间,当前线程不会被阻塞,可以执行其他任务。
在 GCD 中,异步函数的典型代表是 dispatch_async() 函数。该函数接收两个参数,第一个参数是队列,第二个参数是任务。任务是一个 block,其中包含了需要执行的代码。当调用 dispatch_async() 函数时,当前线程不会等待任务执行完毕,而是继续执行后续代码。
区别总结
特性 | 同步函数 | 异步函数 |
---|---|---|
线程阻塞 | 是 | 否 |
执行时机 | 当前线程等待函数执行完毕后 | 当前线程继续执行后续代码 |
代表函数 | dispatch_sync() | dispatch_async() |
任务回调的同步性与异步性
在 GCD 中,任务回调可以是同步的,也可以是异步的。
同步回调
同步回调是指在任务执行完毕后,回调函数会被立即调用。也就是说,在同步回调执行期间,当前线程会被阻塞,无法执行其他任务。
在 GCD 中,同步回调的典型代表是 dispatch_sync() 函数的第二个参数。当任务执行完毕后,回调函数会被立即调用。
异步回调
异步回调是指在任务执行完毕后,回调函数不会被立即调用,而是会被推迟到稍后某个时间点再调用。也就是说,在异步回调执行期间,当前线程不会被阻塞,可以执行其他任务。
在 GCD 中,异步回调的典型代表是 dispatch_async() 函数的第二个参数。当任务执行完毕后,回调函数会被推迟到稍后某个时间点再调用。
区别总结
特性 | 同步回调 | 异步回调 |
---|---|---|
线程阻塞 | 是 | 否 |
执行时机 | 任务执行完毕后立即调用 | 任务执行完毕后推迟到稍后某个时间点再调用 |
代表函数 | dispatch_sync() 的第二个参数 | dispatch_async() 的第二个参数 |
死锁分析
在 GCD 中,如果同步函数在主线程中调用,可能会导致死锁。这是因为主线程是应用程序的主线程,负责处理界面更新和其他重要任务。如果主线程被阻塞,则应用程序将无法正常运行。
为了避免死锁,需要避免在主线程中调用同步函数。如果确实需要在主线程中调用同步函数,则需要使用 dispatch_get_main_queue() 函数获取主队列,然后使用 dispatch_async() 函数将任务提交到主队列中执行。
死锁案例分析
- (void)viewDidLoad {
[super viewDidLoad];
// 在主线程中调用同步函数 dispatch_sync()
dispatch_sync(dispatch_get_main_queue(), ^{
// 模拟耗时操作
sleep(5);
});
// 后续代码无法执行,主线程被阻塞
}
在这个例子中,我们在 viewDidLoad 方法中调用了同步函数 dispatch_sync()。由于 dispatch_sync() 是同步函数,因此当前线程(即主线程)会等待任务执行完毕后才继续执行。由于任务模拟了耗时操作,因此主线程会被阻塞 5 秒。在此期间,主线程无法处理界面更新和其他重要任务,导致应用程序假死。
总结
在本文中,我们探索了同步函数与异步函数的区别,以及任务回调的同步性与异步性。我们还通过死锁的分析案例,帮助你更好地理解同步函数的调用时机。希望这些内容对你理解 GCD 有所帮助。