2025年3月24日 星期一 甲辰(龙)年 月廿三 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > Other

我教我自己: 协程(1)

时间:02-25来源:作者:点击数:10

因为某些原因,我需要写一点Android上的代码,去异步下载一些文件,同时保证UI不卡。

然而,用Kotlin入门写了一些代码以后,愣是卡在协程(Coroutine)这个地方了;下面是我看kotlin官方的一些教程:

Coroutines Overview - Kotlin Programming Language

Coroutines Guide - Kotlin Programming Language​

看完之后一头雾水,主要表现在

  • 啥是coroutine? 和Thread有啥区别?
  • 啥是suspend function?suspend function和coroutine有啥关系?
  • 啥是http://Dispatchers.IO / Dispatchers.Main / Dispatchers.Default? 这些Dispatchers和coroutine有啥关系?
  • 啥是launch { ... } 和 runBlocking { ... }?这俩什么关系?这俩和coroutine有啥关系?
  • 啥是async / await? async / await和coroutine有啥关系?

经过这么一折腾,我已经忘记啥是coroutine了。

这篇文章是参考Wikipedia重新学习和回忆coroutine的一篇手记。


根据Wiki上定义,

协程是能主动让出执行的程序,允许挂起恢复两种操作。Coroutines are computer program components that generalize subroutines for non-preemptive multitasking, by allowing execution to be suspended and resumed.

这个定义就很清楚了,协程有这些重要特征:

  • 主动让出执行。
  • 提供两个原语(Primitives): 挂起(suspend)和恢复(resume)

什么叫做主动让出执行呢?

举一个例子。

你去菜市场买菜,散装菜要称斤打标签之后才能结账。

到了结账的时候,你发现有一袋子西红柿没有称斤;这时候你和服务员说:

让后面的人先结吧。

这就叫主动让出执行。

在程序执行的时候,经常会遇到类似的情况;就是程序需要从网络下载一些图片啊,或者从硬盘上加载一些文件啊,这些操作和CPU操作比起来,都是很耗时间的工作;程序就可以使用挂起(suspend)原语,告诉操作系统,我要干一个很长时间的事儿,让其他人先用CPU吧。这就是程序上的主动让出执行。

由于这种方式有点“商量着来”的意思,所以在操作系统里, 也叫做“协作性调度”(Cooporative multitask)。

与“协作性调度”相对应的,叫做“抢占式调度”(Preemptive multitask)。

加入你是个比较“独”的人,买菜时候不管后面人排队,和服务员说:

我还有一袋子西红柿没有称,让后面人等会儿,我称完回来

于是骂骂咧咧的去称西红柿去了。

五分钟过去,等你回来,发现服务员根本没搭理你,主动把你的菜摆在一边,帮后面的人称东西了。这种大快人心的方式,就叫做“抢占式调度”。

抢占式调度,是现代操作系统调度的默认方式;只要程序一干点跟CPU没关系的事情,立刻就会被赶走,让其他需要用CPU的程序使用。

回到协程,我们就可以回答一点“线程和协程有啥区别”的问题了:

  • 线程使用抢占式调度
  • 协程使用协作性调度

这种调度方式,说明协程提供了并发(concurrency),而不提供并行(paralleism)。这两个名词中文翻译很相似,但是他俩是完全不同的概念:

  • 并发(Concurrency)是“物尽其用”,通过调整任务的顺序,缩短总体使用的时间。用排队买菜的例子比喻,就是调整排队的人的顺序,让全部称完的人先过称,还没有称完的人往后排,先完成称重,再来过称。
  • 并行(Parallelism)是“人多力量大”。增加处理单元的数量,缩短处理时间。用排队买菜的例子比喻,就是从2个称重窗口变成10个称重窗口,同时称。

协程之间的切换是非常确定的,就是在“挂起”(Suspend)和“恢复”(Resume)的时候去切换。因为这个切换是提前在程序里写好的,所以切换是可以预测的,而且消耗资源很少。

对比线程,线程的切换是不确定的;一个线程是否被切换,取决于操作系统的“心情”:是不是有优先级更高的事情发生了,是不是当前程序用CPU太久了,等等。所以要想写一个正确的多线程程序,必须使用一些重量级的同步工具,比如信号量(Semaphore)等;这些线程上下文的切换和同步工具的使用,让线程切换的开销要远远高于协程。

最后总结一下协程和线程的区别:

  • 协程用协作性调度,线程用抢占式调度
  • 协程带来并发(Concurrency),线程带来并行(Parallelism)
  • 协程使用挂起(Suspend)和恢复(Resume)原语切换,线程使用抢占式调度切换上下文,用信号量等同步
  • 协程开销比线程少

下一篇文章讲讲和协程生成器Generator的区别。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐