美文网首页
协程基础

协程基础

作者: 小吉头 | 来源:发表于2020-05-09 11:05 被阅读0次

    一、概念

    协程是通过一个线程在不同代码块之间切换执行,从而实现多任务(这里是并发,假的多任务)
    并发:一个时间段内,几个程序在同一cpu上运行,但是一个时刻点只有一个程序在cpu上运行
    并行:任意时刻点上,多个程序运行在多个cpu上

    二、实现方式

    (1) yield实现

    import time
    
    def task1():
        while True:
            print("---1---")
            time.sleep(1)
            yield
    
    def task2():
        while True:
            print("---2---")
            time.sleep(1)
            yield
    
    
    def task3():
        while True:
            print("---3---")
            time.sleep(1)
            yield
    
    def main():
        t1 = task1()
        t2 = task2()
        t3 = task3()
        while True:
            next(t1)
            next(t2)
            next(t3)
    
    if __name__ == "__main__":
        main()
    

    遇到yield就返回,开始执行下一个生成器。本质是一个线程在三个生成器之间不断切换执行。

    (2) greenlet实现

    yield方式每次都要返回main函数,然后再去进入到另一个代码块,再返回main函数,再进入下一个代码块... greenlet是基于yield的封装, 只要启动greenlet对象后,代码块之间可以自由切换,比yield方便。

    from greenlet import greenlet # 如果没有该模块,通过pip install greenlet
    import time
    
    def task1():
        while True:
            print("---1---")
            g2.switch()
            time.sleep(1)
    
    def task2():
        while True:
            print("---2---")
            g1.switch()
            time.sleep(1)
    
    g1 = greenlet(task1)
    g2 = greenlet(task2)
    
    g1.switch()
    

    (3) 使用gevent

    greenlet还是要人为在代码块中设置切换点,gevent是对greenlet的封装,当线程遇到耗时操作能自动切换到其他代码块,再在适当的时候切换回来继续执行。yield和greenlet不是真正意义的多任务,因为遇到耗时操作(比如代码中的sleep())线程会卡在那边等待延时结束。

    import gevent
    from gevent import monkey
    import time
    
    monkey.patch_all() # 自动将协程代码块中的耗时函数替换成gevent对象中的耗时函数
    
    def g1(n):
        for i in range(n):
            print("g1--->{0}".format(i))
            time.sleep(3)
    
    def g2(n):
        for i in range(n):
            print("g2--->{0}".format(i))
            time.sleep(2)
    
    def g3(n):
        for i in range(n):
            print("g3--->{0}".format(i))
            time.sleep(1)
    
    gevent1 = gevent.spawn(g1,3)#创建协程对象,是对greenlet的封装
    gevent2 = gevent.spawn(g2,3)
    gevent3 = gevent.spawn(g3,3)
    
    gevent3.join()# join()表示阻塞,等待gevent3对象执行完成再返回。这时线程发现gevent3.join()是耗时操作,于是从最先创建的协程gevent1开始执行
                  # 根据创建顺序,gevent1、gevent2、gevent3会轮流执行,遇到耗时操作sleep()时,线程会自动切换,一直到gevent3对应的代码块执行完返回
    print("gevent3结束")
    gevent2.join()
    print("gevent2结束")
    gevent1.join()#此时gevent1代码块已全部执行完毕,无需阻塞
    print("gevent1结束")
    
    #结果如下
    g1--->0
    g2--->0
    g3--->0
    g3--->1
    g2--->1
    g3--->2
    g1--->1
    gevent3结束
    g2--->2
    g1--->2
    gevent2结束
    gevent1结束
    

    相关文章

      网友评论

          本文标题:协程基础

          本文链接:https://www.haomeiwen.com/subject/epiqnhtx.html