iOS多线程 - 死锁原因

Note: 在同一个串行队列中执行同步操作会引发死锁。
-(void)viewdidLoad {
    // 在主线程中进行同步操作(主线程)
   dispatch_sync(dispatch_get_main_queue(), ^{
       // do something
   });
}
要知道死锁的原因,首先要清楚并发、串行队列,异步和同步之间的区别:
  1. 并发队列将任务(函数)分配到线程执行时,不需要等待函数的返回即可执行下一个任务
  2. 串行队列需要等待任务的返回才能执行下一个任务
  3. 同步任务会在当前线程执行,异步任务会在另外的线程执行。

验证一下串行队列:
1、在串行队列中执行同步任务,不出意外的发生了闪退

class func demo01() {       
        let queue = DispatchQueue.init(label: "serial")
        queue.async { // 这里是同步或者异步都不影响,因为下面的同步任务并不会开线程          
            queue.sync { //⚠️  执行同步任务会闪退,异步任务不会
                print("bbb")
            }
            print("ccc")
        }
    }

如上图代码中所示,发生了死锁

  1. 在串行队列中一开始加入了一个异步任务,队列会根据FIFO原则将任务分配到一个线程执行,代码中可以看出 aaa 会先执行打印。
  2. 接着向队列追加一个同步操作的任务(block),该任务会将bbb的打印提交到目标线程。并且由于是同步操作,所以会立即执行,将bbb分配到目标线程中。由于是串行队列,所以不管同步异步都只分配一个线程。(因为任务都是阻塞执行,所以分配多个线程是没有意义的)
  3. 由于需要等待上一个任务执行完毕,虚线部分代表Async_block的执行内容,等执行完ccc的打印后该block便会返回,并且将任务出列。
  4. 而此时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

你可能感兴趣的:(iOS多线程 - 死锁原因)