协程
协程
历史上协程这个概念的出现要早于线程
协作式多任务与抢夺式多任务
进程与线程
一个标准的线程由线程ID,当前指令指针PC,寄存器和堆栈组成。
进程由内存空间(代码,数据,进程空间,打开的文件)和一个或多个线程组成。
进程持有资源,一旦退出,进程申请的各种资源都会被OS强制回收。
而线程依附于进程,资源不和它绑定。
控制流与逻辑流
控制流与逻辑流的统一
线性与非线性
基于回调风格的事件通知机制以及协程机制
异步回调地狱:设置各种回调函数,等待条件就绪后再执行对应回调函数。大量的回调函数也导致写出来的代码支离破碎,难以维护。
因何而生
协程诞生解决的是低速IO和高速CPU的协调问题
thread VS coroutine
线程
- 线程的创建、切换发生在内核态
- 线程的调度不由应用决定(抢占式调度),而是内核决定
协程
- 协程的创建、切换发生在用户态
- 协程的调度由应用自身决定(协作式调度),协程让出执行机会时才会换出;协程确认已经准备好时才会换入
- 操作系统不知道协程的存在
使用协程的线程创建、调度的开销会远低于操作系统线程:
协程的存在对于操作系统而言是不可知的,所以协程也没必要通知内核,协程的切换无需进入内核态进行调度,也无需保留系统线程必须的其他资源,只需要调整上下文即可,切换几个寄存器和少量状态量的开销仅仅是数ns而已;协程是应用自身实现的,协程换出是程序确认需要换出时才会发生,换入也可以到应用确实结束等待时再换入,可以减少无意义的切换
线程与协程最大的区别在是否依赖CPU时钟发出的中断来调度
生命周期
协程的上下文切换
无栈协程
上下文保留在堆中,切换过程不用和栈机制打交道。
有栈协程
保存和切换寄存器,就可以实现栈切换。
汇编实现或者提供了api。
协程本质
协程是一种任务调度机制
协程实质上是一种在用户空间实现的协作式多线程架构
协程不是万能药
IO密集型与CPU密集型
协程并没有解决io密集问题,协程只是优化了异步回调模型增强了代码的可读性。
真正解决io密集问题的是以io多路复用函数以及非阻塞套接字为基础构建的基于事件的响应式编程模式。
只要存在race condition就需要某种形式的锁对数据的访问做互斥。
虽然单线程多协程的代码在表面上看是顺序处理,但不要忘了它的内在是一个并发程序。
race condition适用的场景是并发,而并发跟多线程没有必然的联系。
任务原子性
存在一种隐患:用户的积分在快速变化中。比如一开始用户消费积分50,VIP积分50,但是刚刚查询完消费积分的时候,用户又把消费积分等价换成了VIP积分,这样查询的结果就是50+100 = 150,而不是正确值100。
无论回调还是协程,都无法避免这个问题,不得不在写逻辑时注意任务的原子性。协程从本质上讲并不比回调优越。
除非你确定现在的共享数据不怕被其它协程查看/更改,否则不要在共享数据修改完成前随便放弃你的执行权。
协程的意义
- 控制流和逻辑流的统一
- 在IO操作时避免CPU挂起等待
- 充分利用CPU多核心
性能
回调的性能高于协程
refer:
https://zhuanlan.zhihu.com/p/220025846
https://www.cs.uaf.edu/2016/fall/cs301/lecture/11_18_user_threads.html