• Python并发编程之协程


    一、协程介绍

    协程就是基于单线程实现并发。CPU在运行一个任务的时候,有三种情况下会切换去执行其他任务,第一种是发生了阻塞, 另外一种是该任务计算时间过长,最终一种是有另外一个优先级更高的任务代替了它。

    协程的优点:

    1、开销小,属于程序级别切换,更加轻量
    2、单线程内就可以实现并发的效果,最大限度的利用cpu

    缺点:

    1、本质还是单线程无法利用多核
    2、协程本质还是单线程,一旦出现阻塞会阻塞整个线程

    一个进程会有三种状态,阻塞、运行和就绪。就绪并不能提升效率,它是用来保存状态的。

    yield就是一种在单线程下可以保存任务运行状态的方法。下面我们用yield证明,对于计算密集型的任务协程反倒会降低效率。

    # 并发执行
    import time
    
    
    def producer():
        g = consumer()
        next(g)
        for i in range(1000000):
            g.send(i)
    
    
    def consumer():
        while True:
            res = yield
    
    
    start_time = time.time()
    producer()
    stop_time = time.time()
    print(stop_time - start_time)  # 0.09404683113098145
    
    
    
    # 串行执行
    
    import time
    
    
    def producer():
        res = []
        for i in range(1000000):
            res.append(i)
        return res
    
    
    def consumer(res):
        pass
    
    
    
    start_time = time.time()
    res = producer()
    consumer(res)
    stop_time = time.time()
    print(stop_time - start_time)  # 0.07196402549743652

    总结:遇到IO操作时在使用协程。

    二、greenlet模块

    greenlet封装了生成器(yield、send),可以非常简单的实现多达20个任务的直接切换。但是它遇到什么都切,并不能遇到IO才切,所以意义不大。

    from greenlet import greenlet
    import time
    
    
    # 并不能遇到I/O才切,意义不大
    
    def eat(name):
        print('%s eat 1' % name)
        time.sleep(10)   #  遇到IO就卡住了
        g2.switch('edward')  # 吃一口切到play去玩
        print('%s eat 2' % name)
        g2.switch()
    
    
    def play(name):
        print('%s paly 1' % name)
        g1.switch()
        print('%s paly 2' % name)
    
    
    g1 = greenlet(eat)
    g2 = greenlet(play)
    
    g1.switch('edward')

    三、Gevent

    gevent是一个第三方库,可以轻松实现并发同步或异步编程,主要模式是Greenlet。它是以C扩展模块形式接入Python的轻量级协程,全部运行在操作系统进程的内部。

    用法

    g1=gevent.spawn(func,1,,2,3,x=4,y=5)创建一个协程对象g1,spawn括号内第一个参数是函数名,如eat,后面可以有多个参数,可以是位置实参或关键字实参,都是传给函数eat的
    
    g2=gevent.spawn(func2)
    
    g1.join() #等待g1结束
    
    g2.join() #等待g2结束
    
    #或者上述两步合作一步:gevent.joinall([g1,g2])
    
    g1.value#拿到func1的返回值

    代码示例

    from gevent import monkey;monkey.patch_all()
    
    import gevent
    import time
    
    
    # 换成time.sleep()就不能切了,也就是gevent无法识别其他的I/O操作,只能实现自己的。这就需要用monkey.patch_all()来进行标识,才能用time.sleep()
    def eat(name):
        print('%s eat 1' % name)
        time.sleep(3)
        print('%s eat 2' % name)
    
    
    def play(name):
        print('%s paly 1' % name)
        time.sleep(4)
        print('%s paly 2' % name)
    
    
    start_time = time.time()
    g1 = gevent.spawn(eat, 'edward')  # 只是异步提交了
    g2 = gevent.spawn(play, 'alex')
    
    gevent.joinall([g1, g2])
    
    stop_time = time.time()
    
    print(start_time - stop_time)  # -4.006382703781128

  • 相关阅读:
    20145303刘俊谦 《Java程序设计》第三周学习总结
    20145303刘俊谦 《Java程序设计》第2周学习总结
    MWeb Lite以及Eclipse的使用感想
    学号20145303 《Java程序设计》第一周学习总结
    问卷调查
    20145235 《Java程序设计》第5周学习总结
    20145235 《Java程序设计》第4周学习总结
    20145235李涛 《Java程序设计》第3周学习总结
    20145235 学号 《Java程序设计》第2周学习总结
    20145235李涛《Java程序设计》第一周学习总结
  • 原文地址:https://www.cnblogs.com/lshedward/p/10250886.html
Copyright © 2020-2023  润新知