bennyhuo / Bennyhuo

bennyhuo.vercel.app
8 stars 3 forks source link

破解 Kotlin 协程(6) - 协程挂起篇 | Bennyhuo #20

Open bennyhuo opened 5 years ago

bennyhuo commented 5 years ago

https://www.bennyhuo.com/2019/05/07/coroutine-suspend/

协程的挂起最初是一个很神秘的东西,因为我们总是用线程的概念去思考,所以我们只能想到阻塞。不阻塞的挂起到底是怎么回事呢?说出来你也许会笑~~(哭?。。抱歉这篇文章我实在是没办法写的更通俗易懂了,大家一定要亲手实践!)

richardissuperman commented 5 years ago

您好老师,看完你的原理解析之后,我的理解是只要协程中有一个suspend,状态机就会加1对么?

bennyhuo commented 5 years ago

@richardissuperman 您好老师,看完你的原理解析之后,我的理解是只要协程中有一个suspend,状态机就会加1对么?

嗯,对

landingpencil commented 5 years ago

大佬写得很好👍🏿👍🏽👍🏻 (suspend :vt. 延缓,推迟;使暂停;使悬浮;指暂时中断以待某种条件的实现) 感觉翻译成暂停才好点。线程启动协程,协程代码块不会阻塞线程,suspend会阻塞协程代码块但不会阻塞线程代码块。

bennyhuo commented 5 years ago

@landingpencil 大佬写得很好👍🏿👍🏽👍🏻 (suspend :vt. 延缓,推迟;使暂停;使悬浮;指暂时中断以待某种条件的实现) 感觉翻译成暂停才好点。线程启动协程,协程代码块不会阻塞线程,suspend会阻塞协程代码块但不会阻塞线程代码块。

suspend 这个词本意其实就是悬挂的意思,所以一般说吊桥什么的都是 suspend 相关的衍生词,进而引申到暂停的含义。实际上『挂起』这个词已经广泛应用于操作系统调度啦。

landingpencil commented 5 years ago

@enbandari

@landingpencil 大佬写得很好👍🏿👍🏽👍🏻 (suspend :vt. 延缓,推迟;使暂停;使悬浮;指暂时中断以待某种条件的实现) 感觉翻译成暂停才好点。线程启动协程,协程代码块不会阻塞线程,suspend会阻塞协程代码块但不会阻塞线程代码块。

suspend 这个词本意其实就是悬挂的意思,所以一般说吊桥什么的都是 suspend 相关的衍生词,进而引申到暂停的含义。实际上『挂起』这个词已经广泛应用于操作系统调度啦。

666大佬知识面广

bennyhuo commented 5 years ago

@landingpencil 别提了。。。以前考研背单词记不住,天天搞这些词根记忆法啥的。。嗯,忘得差不多了其实。。

luozejiaqun commented 4 years ago

按照老罗(Roman)的说法,为函数添加suspend关键字并不会天然地使得函数变成“挂起”,“非阻塞”的,我理解的是,“挂起”的黑魔法最关键的在于dispatcher,就好像例子中returnSuspended需要开线程一样,不开线程的话,Thread.sleep照样会阻塞当前的协程,那是不是说,“挂起”只是把“阻塞”转移到别的线程了呢?

bennyhuo commented 4 years ago

@luozejiaqun 其实是的,总会有人阻塞,dispatcher 在每一个挂起点恢复的时候都会去处理切线程的操作。不过换个角度呢,如果我们的底层是一个事件驱动的单线程机制,这个suspend就有意义了,我去注册个回调,你等事件ok了再通知我,这时候suspend就很切题了。

如果简单的理解成阻塞转移,那么看上去协程真的很没用啊,其实阻塞的问题是系统 API 的问题,协程虽然不能解决阻塞的问题,但可以让资源得到更加充分的利用。

当前 Jvm 的实现,确实就是做了个“阻塞”的线程转移,这里面也有一些细节可以探讨,例如我们的业务逻辑是在 UI 线程当中,我通过suspend将转给了专门处理 IO 的线程,这些处理 IO 的线程其实是很多业务逻辑共享的,他们在调用了非阻塞的系统 API 之后,会理解被释放进而去处理其他业务逻辑,这时候阻塞其实是不一定存在的,系统 API 如果采用 epoll,那就跟回调的逻辑是类似的,IO 事件准备好的时候才会通知这个回调,你就会发现其实根本没有所谓的“阻塞”了,这种情况使用协程才会真正轻量级,因为一个协程比一个线程便宜多了。

luozejiaqun commented 4 years ago

@enbandari @luozejiaqun 其实是的,总会有人阻塞,dispatcher 在每一个挂起点恢复的时候都会去处理切线程的操作。不过换个角度呢,如果我们的底层是一个事件驱动的单线程机制,这个suspend就有意义了,我去注册个回调,你等事件ok了再通知我,这时候suspend就很切题了。

如果简单的理解成阻塞转移,那么看上去协程真的很没用啊,其实阻塞的问题是系统 API 的问题,协程虽然不能解决阻塞的问题,但可以让资源得到更加充分的利用。

当前 Jvm 的实现,确实就是做了个“阻塞”的线程转移,这里面也有一些细节可以探讨,例如我们的业务逻辑是在 UI 线程当中,我通过suspend将转给了专门处理 IO 的线程,这些处理 IO 的线程其实是很多业务逻辑共享的,他们在调用了非阻塞的系统 API 之后,会理解被释放进而去处理其他业务逻辑,这时候阻塞其实是不一定存在的,系统 API 如果采用 epoll,那就跟回调的逻辑是类似的,IO 事件准备好的时候才会通知这个回调,你就会发现其实根本没有所谓的“阻塞”了,这种情况使用协程才会真正轻量级,因为一个协程比一个线程便宜多了。

协程把回调拍平了,捋直了,就已经足够有用了,顺带着还能提高资源的利用率。只是很多地方总是在强调协程是“挂起”的,“非阻塞”的,的确有些唬人,至少唬了我好长时间,表象之下还是没有那么简单啊。我觉得这就是协程虽然看上去还挺直观,但是用起来还是不那么踏实的地方,它没有看上去那么简单。

bennyhuo commented 4 years ago

@luozejiaqun 哈哈,同被唬,扒完源码其实就发现“阻塞“和“挂起“都是针对当前协程说的,IO 挂起不挂起,还是得看系统 API。不过你这么一说倒是提醒我了,后面可以优化下措辞,只要跟大家讨论之后才能明白大家在哪里还有疑惑 ~ 而且你这拍平了捋直了这个说法很形象!