zoukankan      html  css  js  c++  java
  • python特有的协程

    #转载请联系

    什么是协程呢?

    线程包含在进程里面,协程包含在线程里面。协程也是和进程、线程一样,可以实现多任务。协程的切换开销比线程更小,不需要保存和恢复线程的状态。最通俗易懂的说法就是,协程是就是一个可以暂停、可以挂起的函数。

    说到可以暂停,可以挂起,我们肯定第一时间想起yield。其实yield生成器就能实现协程。

    import time
    
    
    def work1():
        while True:
            print("我是任务1")
            yield
            time.sleep(1)
    
    
    def work2():
        while True:
            print("我是任务2")
            yield
            time.sleep(1)
    
    
    w1 = work1()  # 取得work1函数的生成器
    w2 = work2()  # 取得work2函数的生成器
    
    while True:
        next(w1)
        next(w2)
    
    输出:
    我是任务1
    我是任务2
    我是任务1
    我是任务2
    我是任务1
    我是任务2
    ......
    ......
    ......
    ......

    你看,在一个进程里面,没有开线程,只有一个主进程主线程,也能实现多任务。因此说协程也能实现多任务,实现的方式是通过yield生成器来实现的。我们先唤醒第一个生成器,执行到yield,暂停。然后我们再唤醒第二个生成器,执行到yield,暂停。再唤醒第一个生成器,依次循环。(用while True来实现依次唤醒)

    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    如果你觉得yield太难了,不知道怎么用。那么,你可以用greenlet模块中的greenlet类来实现协程。这个类是对yield的封装,相对而言比较简单。

    import time
    
    import greenlet
    
    def work1():
        while True:
            print("我是任务1")
            g2.switch()  # 转换到任务2
            time.sleep(1)
    
    def work2():
        while True:
            print("我是任务2")
            g1.switch()  # 转换到任务1
            time.sleep(1)
    
    g1 = greenlet.greenlet(run=work1)
    g2 = greenlet.greenlet(run=work2)
    g1.switch()  # 转换到任务1,其实就是开始执行任务1的意思
    
    输出:
    我是任务1
    我是任务2
    我是任务1
    我是任务2
    我是任务1
    我是任务2
    ......
    ......

    greenlet类虽然能实现协程,但是每个任务都要用switch()方法转换到另一个任务。如果很多很多任务,每个任务都要带这么一段代码,是不是很烦。还要考虑切换顺序什么的。所以,greenlet又被封装到gevent模块的spawn类里。

    -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    gevent原理是当一个greenlet遇到IO(指的是input output 输入输出,比如网络、文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。这个切换原理是和线程、进程的切换原理是一样的。

    import gevent
    import time
    from gevent import monkey  # 这个类一定要这样子导入才能识别!
    
    monkey.patch_all()  # 如果不打这个补丁的话,系统是识别不了原生python代码的time.sleep的
                                     # 所以就不能按在适当的时候切换任务
    
    def work1(n):
        i = 0
        while True:
            print("我是任务1")
            time.sleep(1)
            i += 1
            if i>n:
                break
    
    def work2(n):
        i = 0
        while True:
            print("我是任务2")
            time.sleep(2)
            i += 1
            if i>n:
                break
    
    g1 = gevent.spawn(work1,4)  # 开启g1协程,且阻塞主线程,直到g1协成任务执行完毕,再解主线程的阻塞
    g2 = gevent.spawn(work2,4)
    g1.join()
    g2.join()
    print("我会在哪里?")
    
    输出:
    我是任务1
    我是任务2
    我是任务1
    我是任务2
    我是任务1
    我是任务1
    我是任务2
    我是任务1
    我是任务2
    我是任务2
    我会在哪里?

    注意点:

    1.用gevent创建协程时,如果没有导入monkey模块,打补丁的话,是不能识别原生的python代码的休眠time.sleep的。

    2.monkey模块的导入,一定要from gevent import monkey

    3.join方法既可以启动协程,又可以阻塞主线程,等到该协程完成任务后再解阻塞。

  • 相关阅读:
    redis之 Redis常用数据类型
    mysql5.6之 传输表空间迁移表或恢复误删除的表
    mysql之 double write 浅析
    网络防火墙实战-基于pfsense(1)
    信息战(四)——战场演练(线段树,树状数组)
    dojo(四):ajax请求
    网络防火墙实战-基于pfsense(2)
    [置顶] 最小生成树Prim算法
    (DP6.1.2.1)UVA 147 Dollars(子集和问题)
    Redis的Time Event与File Event的微妙关系
  • 原文地址:https://www.cnblogs.com/chichung/p/9544566.html
Copyright © 2011-2022 走看看