• python学习笔记 day40 协程


    1. 协程

    进程是CPU资源分配的最小单位; 线程是CPU调度的最小单位;

    协程就是在一个线程内切换执行任务;

    之前使用yield也可以实现在一个主线程中切换执行:

    def func1():  # 生成器函数
        print("吃饭吗")
        yield
        print("那我们走吧")
        yield
    
    def func2():
        g=func1()  # 生成器函数在被调用时不会立即执行,除非next(g)触发才可以
        next(g)    # 开始执行func1()函数,但是遇到yield就会停止
        print("")
        next(g)
        print("好的,走吧")
    
    func2()

    使用yield实现生产者消费者模型;

    import random
    import time
    def consumer():  # 消费者模型
        while True:
            n=yield   # yield接收g.send()的结果,然后赋值给n
            time.sleep(random.random())
            print("消费了%s"%n)
    
    def procuder():  # 生产者模型
        g=consumer()  # 调用生成器函数,并不会理解执行生成器函数内部的代码(除非next()进行触发)
        next(g)  # 开始执行生成器函数
        for i in range(10):
            print("生产了%s"%i)
            g.send(i)  # send()给生成器函数yield处接收
    procuder()

    运行结果::

     2. greenlet 实现同一线程内切换

    from greenlet import greenlet
    def func1():
        print("hello,xuanxuan")
        g2.switch()   # 切换执行func2()
        print("byte~ xuanxuan")
        g2.switch()
    def func2():
        print("hello,xixi")
        g1.switch()  # 切换执行func1()
        print("bye~")
    g1=greenlet(func1)
    g2=greenlet(func2)
    g1.switch()  # 切换执行func1

    运行结果:

     另外需要注意greenlet切换时是不规避IO时间的:

    from greenlet import greenlet
    import time
    def func1():
        print("func1:吃饭去吗")
        g2.switch()
        time.sleep(2)   # greenlet进行切换时,并不会规避掉IO时间(也就是切换回来时还是需要等待2秒在执行)
        print("func1:那我们走吧")
        g2.switch()
    
    def func2():
        time.sleep(2)
        print("func2:吃")
        g1.switch()
        time.sleep(3)
        print("func2:好的")
    g1=greenlet(func1)
    g2=greenlet(func2)
    g1.switch()

    运行结果: 

     

    如果在同一个程序有IO的情况下,才切换会让效率提高很多,但是yield greenlet均不会在切换时规避掉IO时间

    其实就相当于原来func1需要等待3秒,但是如果不切换,顺序执行,那么func2也会跟着等待3秒,浪费了两个人的时间,但是如果此时func1这时切换到让func2执行,就可以不让fun2等待func1需要等待的时间,就可以某种程度上实现并发(有点牵强,但是确实有一种func1 func2交替执行,你一句我一句这种并发的效果)

     3. gevent---实现协程切换

    import gevent
    
    def func1():
        print("吃饭去吗")
        gevent.sleep(2)  # gevent可以在gevevt.sleep()自己认识的IO操作切换
        print("那我们走吧")
        gevent.sleep(2)
    def func2():
        print("")
        gevent.sleep(2)
        print("好的")
    g1=gevent.spawn(func1)
    g2=gevent.spawn(func2)
    # g1.join()  # 主线程等待结束
    # g2.join()
    gevent.joinall([g1,g2])  # 相当于上面的g1.join()  g2.join()

    gevent 就是当遇到gevent.sleep() IO 时会自动切换;

    gevent()对普通的IO (比如time模块的sleep,socket 以及urllib request等网络请求)是无法切换的:

    import gevent
    import time
    def func1():
        print("func1:吃饭去吗")
        time.sleep(2)  # gevent对普通模块的IO比如time.sleep()是无法完成切换的
        print("func1:那我们走吧")
        time.sleep(1)
    def func2():
        print("func2:吃")
        time.sleep(2)
        print("func2:好的")
    g1=gevent.spawn(func1)
    g2=gevent.spawn(func2)
    gevent.joinall([g1,g2])  # 等待所有函数执行完 因为主线程跟使用gevent 切换函数执行是完全异步的

    运行结果:

    那如果想在使用gevent在单线程执行函数内遇到其他模块的IO(time.sleep()  socket模块的accept recv 以及urllib requests的网络请求)之间切换 可以在所有IO请求的模块上面加上一句: from gevent import monkey;monkey.patch_all() 即可实现gevent在单线程 执行函数内遇到其他模块的IO 也可以完成切换:

    from gevent import monkey;monkey.patch_all()  # 就可以使用gevent在单线程执行函数内遇到其他模块的IO操作时也可以完成切换
    # 但是这句话必须加在其他模块的最上面(socket time等包含IO的模块)
    import time
    import gevent
    def func1():
        print("func1:吃饭去吗")
        time.sleep(2)  
        print("func1:那我们走吧")
        time.sleep(1)
    def func2():
        print("func2:吃")
        time.sleep(2)
        print("func2:好的")
    g1=gevent.spawn(func1)
    g2=gevent.spawn(func2)
    gevent.joinall([g1,g2])  # 等待所有函数执行完 因为主线程跟使用gevent 切换函数执行是完全异步的

    运行结果:

    talk is cheap,show me the code
  • 相关阅读:
    检查c# 内存泄漏
    条件请求与区间请求
    python排序算法
    webpack+react+redux+es6
    Wireshark命令行工具tshark
    Web的形式发布静态文件
    PyQT制作视频播放器
    DotNet二维码操作组件ThoughtWorks.QRCode
    给你讲个笑话,我是创业公司CEO
    分库分表总结
  • 原文地址:https://www.cnblogs.com/xuanxuanlove/p/9798451.html
Copyright © 2020-2023  润新知