协程-以Python和Go为例

进程、线程和协程进程的定义:

进程,是计算机中已运行程序的实体。程序本身只是指令、数据及其组织形式的描述,进程才是程序的真正运行实例。

线程的定义:

操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。

进程和线程的关系:

一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

协程的定义:

协程通过在线程中实现调度,避免了陷入内核级别的上下文切换造成的性能损失,进而突破了线程在IO上的性能瓶颈。

协程和线程的关系

协程是在语言层面实现对线程的调度,避免了内核级别的上下文消耗。

Python的协程

Python的协程源于yield指令。yield有两个功能:

yield item用于产出一个值,反馈给next()的调用方。

作出让步,暂停执行生成器,让调用方继续工作,直到需要使用另一个值时再调用next()。

协程是对线程的调度,yield类似惰性求值方式可以视为一种流程控制工具,实现协作式多任务。协程的yield语句写在表达式右边(func = yield),可以产出值,也可以不产出值,如果yield后面没有表达式,则生成器产出None。协程也可以从调用方接受数据如send(data)。

在语言层面对协程的引用来源于pep342,pep342详细介绍了协程的使用:

Coroutines are a natural way of expressing many algorithms, such as simulations, games, asynchronous I/O, and other forms of event-driven programming or co-operative multitasking.

重要的几点大概如下:

Redefine yield to be an expression, rather than a statement。

将yield视为一个表达式而不是一个指令

Add a new send() method for generator-iterators, which resumes the generator and sends a value that becomes the result of the current yield-expression.

增加send方法启动生成器和发送值

Add a new throw() method for generator-iterators, which raises an exception at the point where the generator was paused, and which returns the next value yielded by the generator, raising StopIteration if the generator exits without yielding another value.

增加throw方法抛出异常

Add a close() method for generator-iterators, which raises GeneratorExit at the point where the generator was paused.

增加close方法用于退出

在Python3.5正式引入了 Async/Await表达式,使得协程正式在语言层面得到支持和优化,大大简化之前的yield写法。

例如:

Go的协程

Go的协程是天生在语言层面支持,和Python类似都是采用了关键字,而Go语言使用了go这个关键字,可能是想表明协程是Go语言中最重要的特性。

但是上面的程序并不会打印出结果,因为程序没有等待Add函数结束,就已经结束了。而协程之间的通信,Go采用了channel关键字。

协程的调度理论模型

协程的思想源于进程和协程都是属于系统内核级的,开销特别巨大,并且在并发模式下需要各种各样的锁来保证程序运行正常。后来在Node.js里引入了基于回调的非阻塞/异步IO,但是给开发者带来了无尽的“回调地狱”:

也正是因为如此“丑陋”的代码存在,Python和Go引入了消息调度系统模型,也就是协程,来避免锁的影响和进程/线程开销大的问题。协程从本质上来说是一种用户态的线程,不需要系统来执行抢占式调度,而是在语言层面实现线程的调度。具体的消息调度模型可以参考我之前写的文章

Actor模型速览

因为协程不再使用共享内存/数据,而是使用通信来共享内存/锁,因为在一个超级大系统里具有无数的锁,共享变量等等会使得整个系统变得无比的臃肿,而通过消息机制来交流,可以使得每个并发的单元都成为一个独立的个体,拥有自己的变量,单元之间变量并不共享,对于单元的输入输出只有消息。开发者只需要关心在一个并发单元的输入与输出的影响,而不需要再考虑类似于修改共享内存/数据对其它程序的影响。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180805G0GIKO00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券