1 什么是协程?

协程(coroutine)是一种程序运行的方式即在单线程里多个函数并发地执行.

A coroutine is a function that can suspend its execution (yield) until the given given YieldInstruction finishes.

个人简单的理解是, 在协程中的函数, 如果执行过程中遇到了I/O密集型任务, 被给定的YieldInstruction(让出指令)暂停执行(yield), 交出执行权到其他函数, 直到被给定的YieldInstruction(让出指令)结束, 再继续执行。

2 协程与线程的区别

  1. 由于协程的特性, 适合执行大量的I/O 密集型任务, 而线程在这方面弱于协程
  2. 协程涉及到函数的切换, 多线程涉及到线程的切换, 所以都有执行上下文, 但是协程不是被操作系统内核所管理, 而完全是由程序所控制(也就是在用户态执行), 这样带来的好处就是性能得到了很大的提升, 不会像线程那样需要在内核态进行上下文切换来消耗资源,因此协程的开销远远小于线程的开销
  3. 同一时间, 在多核处理器的环境下, 多个线程是可以并行的,但是运行的协程的函数却只能有一个其他的协程的函数都被suspend, 即协程是并发的
  4. 由于协程在同一个线程中, 所以不需要用来守卫临界区段的同步性原语(primitive)比如互斥锁、信号量等,并且不需要来自操作系统的支持
  5. 在协程之间的切换不需要涉及任何系统调用或任何阻塞调用
  6. 通常的线程是抢先式(即由操作系统分配执行权), 而协程是由程序分配执行权

3 协程的原理

当出现IO阻塞的时候,由协程的调度器进行调度,通过将数据流立刻yield掉(主动让出),并且记录当前栈上的数据,阻塞完后立刻再通过线程恢复栈,并把阻塞的结果放到这个线程上去跑,这样看上去好像跟写同步代码没有任何差别,这整个流程可以称为coroutine,而跑在由coroutine负责调度的线程称为Fiber。比如Golang里的 go关键字其实就是负责开启一个Fiber,让func逻辑跑在上面。

由于协程的暂停完全由程序控制,发生在用户态上;而线程的阻塞状态是由操作系统内核来进行切换,发生在内核态上。
因此,协程的开销远远小于线程的开销,也就没有了ContextSwitch上的开销。

4 协程的应用场景

协程的应用场景主要在于 :I/O 密集型任务

这一点与多线程有些类似,但协程调用是在一个线程内进行的,是单线程,切换的开销小,因此效率上略高于多线程;

当程序在执行 I/O 时操作时,CPU 是空闲的,此时可以充分利用 CPU 的时间片来处理其他任务;

在单线程中,一个函数调用,一般是从函数的第一行代码开始执行,结束于 return 语句、异常或者函数执行(也可以认为是隐式地返回了 None );

有了协程,我们在函数的执行过程中,如果遇到了I/O密集型任务,函数可以临时让出控制权,让 CPU 执行其他函数,等 I/O 操作执行完毕以后再收回其他函数的控制权.

参考来源

https://docs.unity3d.com/540/Documentation/ScriptReference/Coroutine.html

https://zh.wikipedia.org/wiki/协程

https://stackoverflow.com/questions/553704/what-is-a-coroutine

https://juejin.cn/post/6844903921471717389

https://www.cnblogs.com/Survivalist/p/11527949.html