主线程如何进行阻塞?

1,989 阅读4分钟

前言:

今天在群里, 有个小伙伴问了一个问题, 他们和js交互的时候, 需要等待js的一个回调.

主线程调用一个网络请求, 然后要等网络请求的结果回来之后, 然后在return.

他说, 他们之前是根据runloop去回调添加监听解决的.说又引发了一些其他的问题, 问有没有人知道怎么做.

解决

我一直以为这应该是大部分都知道的, 直到他用runloop解决, 很多人都在群里讨论说没法实现, 我才意识到或许并不是那么简单.

方法一

如果你实在是不知道怎么解决, 那么就把return去掉换成block回调, 然后进行调用. 当然这可能会让你去重写一部分逻辑代码, 但是解决起来算是好用的.

方法二

就是在主线程做堵塞.那么问题来了

  • 主线程可不可以做阻塞?
  • 主线程调用async会不会开辟新线程?

主线程调用async会不会开辟新线程?

先说一个基础的async不管是同步队列, 还是异步队列, 调用async都会开辟线程, 但是区别是什么呢? 主线程async会吗?

01.png 建议先把图片放大, 自己思考一下会输出什么.

**1--<NSThread: 0x600001e08880>{number = 1, name = main}**

**3--<NSThread: 0x600001e5cc40>{number = 5, name = (null)}**

**2--<NSThread: 0x600001e08880>{number = 1, name = main}**

主线程异步调用不会开辟新线程, 但是主队列是一个串行队列, 任务异步加入之后一定是先执行完当前的任务, 才会去执行后面的任务.

自定义同步队列怎么打印?

02.png

先分析, 自定义同步队列:

  • 在主线程调用, 所以1,4,5是在主线程. 1-4-5的顺序应该没有疑问
  • 2和3的问题, 2任务是先添加在同步队列queue中, 所以2-3的顺序.
  • 2任务不影响4的执行, 但是3任务阻塞了5任务的执行. 所以1-4-2-3-5其中24并不固定,1最前,3,5最后

03.png 所以, sync调用不会创建线程, 会在当前所在任务的线程, 并且对任务进行阻塞, 等待block_t中的任务执行完毕才继续进行当前所在任务.

自定义异步队列怎么打印?

04.png 分析一下:

根据上面的问题, 大概知道了任务在线程中跑的逻辑

  • 首先可以确定,1-3-4-5是在主线程, 或者demo4在哪个线程调用就是哪个线程.
  • 2的任务是异步队列, 异步执行, 在另一条线程中无拘无束. 所以最终的打印是: 1-3-4-5, 2只在1之后的任意位置.

05.png

主线程阻塞解决方案

问题代码:

06.png 大概就是这么一个问题, 主线程调用了一个方法, 需要等web的回调. 然而, 主线程不能用信号量卡, 一卡就会卡死, 所以群里的人说runloop的解决要换掉, 有没有好用的方式.

可以先思考三秒, 这样打印可能是10也可能是15, 这都不一定, 但是大概率是10:

直接给出答案了, 因为我觉得多写写线程, 队列, 锁之类的这些都是常识性的东西, 并不是很难理解:

07.png 还有其他的实现方案, 这里只是提供一种思路, 如果看懂了, 其实还可以在想想其他的. 这也充分说明了,一个线程多个队列, 一个队列多个任务.

注意, 不可以在这里面去添加getMainQueuesync的任务, 不然会任务相互堵塞, 我试用了, 我们项目的一个第三方, 他的请求内部调用了HUD所以这种的会直接任务相互卡, 界面就会卡死.

下图可以看出线程和队列的关系:

08.png

后续我会把GCD源码的重要点详细列出来.

总结

世界存在的意义不就是发现问题和解决问题吗, 所以遇见问题才是敲代码的意义.