对协程的一点理解
什么是协程
A coroutine is a function that can suspend its execution (yield) until the given YieldInstruction finishes.
也就是说,协程是一个函数,可以被挂起和被恢复。
协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。这样带来的好处就是性能得到了很大的提升,不会像线程那样需要上下文切换来消耗资源,因此协程的开销远远小于线程的开销。
子程序
子程序,就是函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。
协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。
注意,在一个子程序中中断,去执行其他子程序,不是函数调用,有点类似CPU的中断。比如子程序A、B:
def A(): print '1' print '2' print '3' def B(): print 'x' print 'y' print 'z'
假设由协程执行,在执行A的过程中,可以随时中断,去执行B,B也可能在执行过程中中断再去执行A,结果可能是:
1 2 x y 3 z
但是在A中是没有调用B的,所以协程的调用比函数调用理解起来要难一些。
看起来A、B的执行有点像多线程,但协程的特点在于是一个线程执行,那
和多线程比,协程有何优势?
1. 切换开销小,执行效率高
因为子程序切换不是线程切换,而是由程序自身控制
2. 不需要多线程的锁机制
因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了
子程序与协程的差异:
协程可以通过yield(取其“让步”之义而非“出产”)来调用其它协程,接下来的每次协程被调用时,从协程上次yield返回的位置接着执行,通过yield方式转移执行权的协程之间不是调用者与被调用者的关系,而是彼此对称、平等的
1. 子程序可以调用其他子程序,调用者等待被调用者结束后继续执行,故而子程序的生命期遵循后进先出,即最后一个被调用的子例程最先结束返回。协程的生命期完全由对它们的使用需要来决定。(也就是想怎么跳就怎么跳,不需要遵循函数调用那套规则)
2. 子程序的起始处是惟一的入口点,而协程可以有多个入口点,协程的起始处是第一个入口点,每个yield返回出口点都是再次被调用执行时的入口点。
3. 子例程只在结束时一次性的返回全部结果值。协程可以在yield时不调用其他协程,而是每次返回一部分的结果值,这种协程常称为生成器或迭代器。
子例程是协程的特里,因为任何子例程都可以看作是不调用yield的协程
示例
这里是一个简单的例子证明协程的实用性。
传统的生产者-消费者模型是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待,但一不小心就可能死锁。
如果改用协程,生产者生产消息后,直接通过yield跳转到消费者开始执行,待消费者执行完毕后,切换回生产者继续生产,效率极高:
var q := 新建队列 coroutine 生产者 loop while q 不满载 建立某些新产品 向 q 增加这些产品 yield 给消费者 coroutine 消费者 loop while q 不空载 从 q 移除某些产品 使用这些产品 yield 给生产者
应用场景
根据今天查阅的资料来看,协程的应用场景主要在于 :I/O 密集型任务。
参考链接
相关文章
- [C# 基础知识系列]专题八: 深入理解泛型(二)
- RNN,LSTM,GRU基本原理的个人理解
- ContentResolver + SqliteOpenHelper + ContentProvider 理解
- 【mysql】一对一关系的理解,以及Navicat Premium怎么设置字段的唯一性(UNIQUE)?
- net.ipv4.tcp_fin_timeout的错误理解
- 谈谈对分布式事务的一点理解和解决方案
- 自然语言处理NLP星空智能对话机器人系列:深入理解Transformer自然语言处理 Text completion with GPT-2 step 9
- 深入理解计算机系统之旅(一)计算机系统漫游
- 深入理解C# 3.x的新特性(2):Extension Method[上篇]
- 湖畔大学教育长曾鸣:把“五新”放在一起,才能理解新零售的未来
- 学习如何理解代码以成为更优秀的程序员
- Java反序列化漏洞从理解到实践
- 深入理解NLP的多项选择任务
- 理解C语言——从小菜到大神的晋级之路(15)——完结篇:C编程风格