我们都知道,在主线程上,同步执行,为主队列添加 task 会导致卡死。
到底该怎么理解呢?
第一
主线程有一个特点:主线程会先执行主线程上的代码片段,然后才会去执行放在主队列中的任务。
第二
同步执行 dispatch_sync函数的特点:该函数只有在该函数中被添加到某队列的某方法执行完毕之后才会返回。即 方法会等待 task 执行完再返回
具体分析:
dispatch_sync 函数本身是放在主线程中执行的,也就是说他本身也是属于主线程执行任务的一部分。根据主线程的特点:主线程会等主线程上的代码执行完毕之后才会去执行放置到主队列中的 task;再根据 disptach_sync 函数特点, task 不执行完毕,dispatch_sync 函数不返回。这样,dispatch_sync 为了返回会等 task 执行完毕也就是主线程执行完,而 task 执行又等着主线程上的代码执行完,也即主线程上 dispatch_sync 代码执行完。
主队列中的任务必须按顺序挨个执行
任务要等主线程有空的时候(即主队列中的所有任务执行完)才能执行
主线程要执行完“结束标志”的任务后才有空
“任务”和“结束标志”两个任务互相等待,造成死锁
卡死的原因已经说明清楚了。
现在来证明上面两点
第一:主线程执行特点,先执行主线程上的代码,再执行主队列中的任务。
-------划重点了! 这里强调~~~~~> 主队列 ! 主队列 ! 主队列 !
如果是串行队列,在主线程上同步方法添加一个任务到串行队列。
不会死锁那说明,没有出现上面说的互相等待的问题,我们来分析一下。
首先,dispatch_sync 函数的特性没变,等待 task 执行完毕之后才能返回。
既然,程序能正常运行,那么必然 dispatch_sync 函数等到了 task 的返回。
从结果输出的顺序看出, 同步执行串行队列的实质为
主线程会先执行完主线程上的代码,然后再执行主队列中的方法!!
我们怎么证明呢?要知道在主线程上同步为主队列添加 task 会死锁。
那么,我们在主线程上异步为主队列添加 task
结果好像是按照我们预期那样执行的,先执行主线程上代码在执行主队列中 task。
可是,仅仅这样就判定主线程会先执行主线程中代码再执行主队列中的 task 还不准确。
首先,dispatch_async 函数的功能,我们都会想到,觉得是否开启新的线程去执行队列中的任务。其实还有一点,那就是这个函数本身不会阻塞线程的执行,不同于上述 dispatch_sync,dispatch_sync 会阻塞线程,知道里面的 task 执行完毕后才返回。这个特性在主线程乃至子线程都奏效。
所以,执行到dispatch_async函数时不等待 task 执行直接接着执行后面的代码。考虑到队列中 task 调用问题(这是需要 cpu 调度的,会消耗一定时间),我们在原代码基础之上,dispatch_async函数之后然线程 sleep 一会,看添加到主队列中的 task 是否真正在等待主线程上代码的执行。
继续验证
那么我们就证明了,主线程确实会先执行完主线程上代码再执行主队列中的 task(因为 dispatch_async 不会等待函数中被添加到某队列的某方法执行完毕之后才会返回), 主队列又叫全局串行队列。
我们解决死锁的方法,通常是把死锁方法放到子线程里面执行。
为什么现在就不会死锁了呢?
子线程同步执行,一样阻塞, dispatch_async函数会阻塞下面 NSLog 方法进行输出,dispatch_async函数结束的时刻是里面 task 执行完毕的时刻,
dispatch_async将 task 放入主队列中,主队列等待主线程上代码执行完毕之后,再执行主队列中的 task ,主队列中的 task 执行完毕之后再跳出dispatch_async函数之后子线程最后的 NSLog 方法。
证明:
接下来在子线程上把同步方法换成异步方法呢?
以后我们再见到dispatch_async 或者dispatch_sync时,除了要考虑他们是否会开启新的线程之外,还应该记住:
dispatch_async不阻塞当前线程
dispatch_sync阻塞当前线程