Note: 在同一个串行队列中执行同步操作会引发死锁。
-(void)viewdidLoad {
// 在主线程中进行同步操作(主线程)
dispatch_sync(dispatch_get_main_queue(), ^{
// do something
});
}
要知道死锁的原因,首先要清楚并发、串行队列,异步和同步之间的区别:
- 并发队列将任务(函数)分配到线程执行时,不需要等待函数的返回即可执行下一个任务
- 串行队列需要等待任务的返回才能执行下一个任务
- 同步任务会在当前线程执行,异步任务会在另外的线程执行。
验证一下串行队列:
1、在串行队列中执行同步任务,不出意外的发生了闪退
class func demo01() {
let queue = DispatchQueue.init(label: "serial")
queue.async { // 这里是同步或者异步都不影响,因为下面的同步任务并不会开线程
queue.sync { //⚠️ 执行同步任务会闪退,异步任务不会
print("bbb")
}
print("ccc")
}
}
如上图代码中所示,发生了死锁
- 在串行队列中一开始加入了一个异步任务,队列会根据FIFO原则将任务分配到一个线程执行,代码中可以看出 aaa 会先执行打印。
- 接着向队列追加一个同步操作的任务(block),该任务会将bbb的打印提交到目标线程。并且由于是同步操作,所以会立即执行,将bbb分配到目标线程中。由于是串行队列,所以不管同步异步都只分配一个线程。(因为任务都是阻塞执行,所以分配多个线程是没有意义的)
- 由于需要等待上一个任务执行完毕,虚线部分代表Async_block的执行内容,等执行完ccc的打印后该block便会返回,并且将任务出列。
- 而此时ccc的打印又需要等待bbb的打印,所以两个任务在队列中互相等待。
截屏2021-11-18 下午3.18.30.png
验证一下并发队列:
// 并发队列不需要等待任务返回
let queue = DispatchQueue.init(label: "concurrent",attributes: .concurrent)
queue.sync {
print("aaa \(Thread.current)")
queue.sync { // 并不会发生死锁
print("bbb \(Thread.current)")
}
print("ccc \(Thread.current)")
}
可以看到并没有引发死锁,原因如下:
1、由于并发队列不需要等待任务返回,因此将任务提交到线程执行后就出列了
2、因此执行aaa之前asyncblcok函数已经出列了。
3、所以即使再加入一个同步任务也不会引起阻塞,而是立即提交任务到目标线程执行,并阻塞等待返回后才出队。
4、同时这个demo证明了,死锁是由于队列阻塞而非线程原因引起的。因为两个任务都是同步任务,都在同一个线程上执行。
截屏2021-11-18 下午4.26.41.png