• 协程


    1、协程的理解

    协程,又称微线程,纤程。英文名Coroutine,是一种用户态的轻量级线程。

    注意:
      1. python的线程属于内核级别的,即由操作系统控制调度(如单线程一旦遇到io就被迫交出cpu执行权限,切换其他线程运行)
      2. 单线程内开启协程,一旦遇到io,从应用程序级别(而非操作系统)控制切换
    协程优点:
      1. 协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级
      2. 单线程内就可以实现并发的效果,最大限度地利用cpu
    协程缺点:
      1.协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程
      2.协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程

    协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方(线程调度时候寄存器上下文及栈等保存在内存中),在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。

    子程序,或者称为函数,在所有语言中都是层级调用,比如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的执行有点像多线程,但协程的特点在于是一个线程执行。

    2、yield的运用

    从句法上看,协程与生成器类似,都是定义体中包含 yield 关键字的函数。可是,在协程中, yield 通常出现在表达式的右边(例如, datum = yield),可以产出值,也可以不产出 —— 如果 yield 关键字后面没有表达式,那么生成器产出 None。

    协程可能会从调用方接收数据,不过调用方把数据提供给协程使用的是 .send(datum) 方法,而不是next(…) 函数。

    ==yield 关键字甚至还可以不接收或传出数据。不管数据如何流动, yield 都是一种流程控制工具,使用它可以实现协作式多任务:协程可以把控制器让步给中心调度程序,从而激活其他的协程==。

    import random
    def create_generator_function(n):
        i = 0
        while i < n:
            b = yield i
            print("It's {0}".format(b))
            i = i + 1
    obj = create_generator_function(5)
    obj1 = obj.next()
    x = 0
    while x<5:
        try:
            obj1 = obj.send(random.random()) 
        except:
            pass
        x += 1

    我们先给协程一个标准定义,即符合什么条件就能称之为协程:

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

    基于上面这4点定义,我们刚才用yield实现的程并不能算是合格的线程,不满足最后一条。

     如何实现单线程下并发效果:遇到IO操作就切换,协程之所以能处理大并发,就是由于挤掉了IO操作,使得CPU一直运行。

    关键在于切换出来后,什么时候再切换回去??需要程序自动监测IO操作,IO操作结束就切换回去。

    以上我们是通过yeild实现的协程的功能,yield能实现协程,不过实现过程不易于理解,greenlet是在这方面做了改进。

    3、Greenlet

    Greenlet是python的一个C扩展,来源于Stackless python,旨在提供可自行调度的‘微线程’, 即协程。generator实现的协程在yield value时只能将value返回给调用者(caller)。 而在greenlet中,target.switch(value)可以切换到指定的协程(target), 然后yield value。greenlet用switch来表示协程的切换,从一个协程切换到另一个协程需要显式指定。是一种手动切换

    4、gevent

    gevent是第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。其基本思想是:

    当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。

    由于切换是在IO操作时自动完成,所以gevent需要修改Python自带的一些标准库,这一过程在启动时通过monkey patch完成。

  • 相关阅读:
    WC项目
    团队项目(MVP新能源无线充电管理网站)(总结)
    学期目标
    个人目标、思维导图、不同点
    结对项目——黄金分割点游戏(陈香宇&蔡春燕)
    团队项目(MVP新能源无线充电管理网站)(个人任务2)
    四则运算
    读后疑问
    crontab 定时任务
    mysql主从配置
  • 原文地址:https://www.cnblogs.com/windyrainy/p/10757859.html
Copyright © 2020-2023  润新知