3.4.1 Greenlet

协程

协程,又称微线程,纤程。英文名coroutine。简单说:协程是一种用户态的轻量级线程。

协程拥有自己的寄存器上下文和栈。写成调度切换时,将寄存器上下文和栈保存到其他地方,在且回来的时候,恢复先前保存的寄存器上下文和栈。

因此,协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换句话说:进入上一次离开时所处逻辑流的位置。

协程的优点:

  • 无需线程上下文切换的开销
  • 无需原子操作锁定及同步的开销    #改一个变量就是原子操作
  • 方便切换控制流,简化编程模型
  • 高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。

缺点:

  • 无法利用多核资源:协程的本质是个单线程,它不能同时将单个CPU的多个核用上,协程需要和进程配合才能运行在多核CPU上,当然我们日常所编写的绝大部分应用都没有这个必要,除非是CPU密集型应用。
  • 进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序

下面是一个使用yield实现协程的示例

import time, queue

def consumer(name):    #函数中使用yield语句,函数会变成一个生成器
    print('Starting to sell iphone...')    #2.1输出 3.1输出
    while True:
        new_iphone = yield    #2.2返回 3.2返回
        print('%s bought a iphone %s' % (name, new_iphone))
        #4.1 c1收到参数并输出  5.1 c2收到参数并输出
        time.sleep(1)    #4.2 c1睡1秒 5.2 c2睡1秒

def producer():
    r = c1.__next__()    #2 执行c1
    r = c2.__next__()    #3执行c2
    n = 0
    while n < 5:
        n += 1
        c1.send(n)    #4 c1传参n并返回至c1yield
        c2.send(n)    #5 c2传参n并返回至c2yield
        print('[Producer] is producing iphone %s.' % n)    #6输出

if __name__ == '__main__':    #------>从这里开始
    c1 = consumer('c1')    #生成器c1
    c2 = consumer('c2')    #生成器c2
    p = producer()    #1 实例化并执行

结果

Starting to sell iphone...
Starting to sell iphone...
c1 bought a iphone 1
c2 bought a iphone 1
[Producer] is producing iphone 1.
c1 bought a iphone 2
c2 bought a iphone 2
[Producer] is producing iphone 2.
c1 bought a iphone 3
c2 bought a iphone 3
[Producer] is producing iphone 3.
c1 bought a iphone 4
c2 bought a iphone 4
[Producer] is producing iphone 4.
c1 bought a iphone 5
c2 bought a iphone 5
[Producer] is producing iphone 5.

以上就是使用yield实现一个协程的例子。

协程的标准定义:

  1. 必须在只有一个单线程里实现开发
  2. 修改共享数据不需加锁
  3. 用户程序里自己保存多个控制流的上下文栈
  4. 一个协程遇到IO操作自动切换到其他协程

基于以上4点,上面的示例严格说还不算真正的协程,因为它有一个功能没实现。

Greenlet

greenlet是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator

from greenlet import greenlet
#greenlet手动切换协程
def test1():
    print(12)    #2
    gr2.switch()
    print(34)    #4
    gr2.switch()


def test2():
    print(56)    #3
    gr1.switch()
    print(78)    #5

gr1 = greenlet(test1)    #启动gr1协程
gr2 = greenlet(test2)    #启动gr2协程
gr1.switch()    #1

结果

12
56
34
78

感觉确实用着比generator还简单了呢,但好像还没有解决一个问题,就是遇到IO操作,自动切换,对不对?

posted @ 2020-01-02 17:07  InfiniteCodes  阅读(132)  评论(0编辑  收藏  举报