洗礼灵魂,修炼python(90)– 知识拾遗篇 —— 协程

协程

1.定义

协程,顾名思义,程序协商着运转,并非像线程那样争抢着运营。协程又叫微线程,一种用户态轻量级线程。协程正是1个单线程(3个剧本运维的都以单线程)

 协程拥有和谐的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其余省方,在切回到的时候,恢复生机原先保存的寄存器上下文和栈。

协程能保存上三次调用时的情事(即具有片段情形的2个一定组合),每回经过重入时,就也便是进入上3回调用的情形,换种说法:进入上1回离开时所处逻辑流的地方,看到那

图片 1 

图片 2

 图片 3

 

没错,正是生成器,前边再实例更会尽量的行使到生成器,但注意:生成器 !=
协程

 

 

2.特性

优点:

  • 无需线程上下文切换的开发
  • 不要原子操作锁定及协助实行的开支
  • 方便人民群众切换控制流,简化编制程序模型
  • 高并发+高扩展性+低本钱:二个CPU支持上万的协程都不是题材。所以很适合用于高并发处理。

注:比如修改3个数额的整套操作进程下来唯有五个结实,要嘛已修改,要嘛未修改,中途现身任何不当都会回滚到操作前的情景,那种操作情势就叫原子操作,”原子操作(atomic
operation)是不必要synchronized”,不会被线程调度机制打断的操作;这种操作一旦先河,就直接运转到完工,中间不会有另外context switch
(切换来另二个线程)。原子操作能够是四个步骤,也能够是七个操作步骤,可是其顺序是不得以被打乱,或许切割掉只举行部分。视作全体是原子性的基本。 

 

缺点:

  • 不能运用多核能源:协程的面目是个单线程,它不可能同时将 单个CPU
    的四个核用上,协程须要和进程协作才能运营在多CPU上.当然我们常见所编写的多方用到都没有那几个须求,除非是cpu密集型应用。
  • 拓展围堵(Blocking)操作(如IO时)会堵塞掉全数程序

 

3.实例

 1)用生成器完毕伪协程:

在那后面,相信广大爱人曾经把生成器是如何忘了吧,这里大致复习一下。

开创生成器有多少个放法:

A:使用列表生成器:

图片 4

 

B:使用yield创立生成器:

图片 5

 

做客生成器数据,使用next()或许__next__()方法:

图片 6

 

好的,既然说到此地,就说下,yield能够暂存数据并转载:

图片 7

 

传是传入了,但结果却报错:

图片 8

 

为啥报错呢?首先要说一个知识点,行使next()和send()方法都会取出2个数码,不一致的是send即发送数据又取出上一数额,并且只要要发送数据必须是第二回发送,要是第三遍正是用send,必须写为send(None)才行,不然报错。next(obj) = obj.send(None).

因为yield是暂存数据,每一回next()时将会在终结时的那里阻塞住,下2遍又从那边早先,而发送完,send取数据发现已经截至了,数据已经没了,所以修改报错,

那就是说稍作修改得:

图片 9

 

完美!

 

好的,进入正题了,有了地方的现款,未来现卖应该没难题了:

依然是眼下的劳动者消费者模型 

import time
import queue

def consumer(name):
    print("--->starting eating baozi...")
    while True:
        new_baozi = yield
        print("[%s] is eating baozi %s" % (name,new_baozi))
        #time.sleep(1)

def producer():

    r = con.__next__()
    r = con2.__next__()
    n = 0
    while n < 5:
        n +=1
        con.send(n)
        con2.send(n)
        print("\033[32;1m[producer]\033[0m is making baozi %s" %n )


if __name__ == '__main__':
    con = consumer("c1")
    con2 = consumer("c2")
    p = producer()

  

运维结果:

图片 10

 

先是咱们精晓使用yield创设了三个生成器对象,然后每趟使用时行使new_baozi做3个中间转播站来缓存数据。那正是兑现协程效果了对吗?

前边作者提了一句,yield下是伪协程,那么如何是确实的协程呢?

亟待具备以下原则

  • 必须在只有三个单线程里福寿无疆产出
  • 修改共享数据不需加锁
  • 贰个体协会程遇到IO操作自动切换来其它协程
  • 用户程序里团结保留两个控制流的光景文栈

 

2)gevent协程

先是其实python提供了3个正式库格林let正是用来搞协程的

#!usr/bin/env python
#-*- coding:utf-8 -*-

# author:yangva

from greenlet import greenlet

def test1():
    print(1)
    gr2.switch() #switch方法作为协程切换
    print(2)
    gr2.switch()

def test2():
    print(3)
    gr1.switch()
    print(4)

gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()

  

运作结果:

图片 11

 

而是效果倒霉,无法知足IO阻塞,所以一般意况都用第壹方库gevent来贯彻协程:

#!usr/bin/env python
#-*- coding:utf-8 -*-

# author:yangva

import gevent,time

def test1():
    print(1,time.ctime())
    gevent.sleep(1)     #模拟IO阻塞,注意此时的sleep不能和time模块下的sleep相提并论
    print(2,time.ctime())

def test2():
    print(3,time.ctime())
    gevent.sleep(1)
    print(4,time.ctime())

gevent.joinall([
    gevent.spawn(test1), #激活协程对象
    gevent.spawn(test2)
])

  

运维结果:

图片 12

 

那正是说只要函数带有参数怎么搞呢?

#!usr/bin/env python
#-*- coding:utf-8 -*-

# author:yangva

import gevent


def test(name,age):
    print('name:',name)
    gevent.sleep(1)     #模拟IO阻塞
    print('age:',age)


gevent.joinall([
    gevent.spawn(test,'yang',21), #激活协程对象
    gevent.spawn(test,'ling',22)
])

  

运转结果:

图片 13

 

 假使你对那几个体协会程的快慢觉得不精粹,能够加上上面这一段,其余不变:图片 14

 

 这个patch_all()也正是一个检测机制,发现IO阻塞就即刻切换,不需等候什么。那样可以节约一些岁月

 

 好的,协程解析完成。

 

相关文章