zoukankan      html  css  js  c++  java
  • python并发编程之gevent协程(四)

    协程的含义就不再提,在py2和py3的早期版本中,python协程的主流实现方法是使用gevent模块。由于协程对于操作系统是无感知的,所以其切换需要程序员自己去完成。

    系列文章

    基于生成器的简单协程

    import time
    def A():
        for i in range(100):
            print("----A---")
            yield i
            time.sleep(0.5)
    
    
    def B(c):
        while True:
            print("----B---")
            try:
                next(c)
            except StopIteration:
                break
            else:
                time.sleep(0.5)
    
    if __name__ == '__main__':
        a = A()
        B(a)
    

    上面的例子并没有带来代码效率的提高,因为time.sleep()是同步阻塞操作;上例主要是为了说明协程的上下文切换原理。

    greenlet的协程

    greenlet模块也是程序显性主动控制协程切换,但是对原生做了一定的封装使得切换变得简单一些。

    from greenlet import greenlet
    import time
    
    def test1(gr,g):
        for i in range(100):
            print("---A--")
            gr.switch(g, gr) # 切换到另一个协程执行
            time.sleep(0.5)
    
    def test2(gr, g):
        for i in range(100):
            print("---B--")
            gr.switch(g, gr)
            # gr.throw(AttributeError)
            time.sleep(0.5)
    
    if __name__ == '__main__':
        # 创建一个协程1
        gr1 = greenlet(test1)
        # 创建一个协程2
        gr2 = greenlet(test2)
        # 启动协程
        gr1.switch(gr2, gr1)
    

    greenlet类主要有两个方法:

    • switch:用来切换协程;

    • throw():用来抛出异常同时终止程序;

    gevent模块协程

    • gevent是在greenlet的基础上进行封装使得gevent变得更加的易用。

    • gevent采用了隐式启动事件循环,即在需要阻塞的时候开启一个专门的协程来启动事件循环;

    • 如果一个任务没有io操作,那么他会一直执行直到完成;其他协程没有执行的机会;

    • 自动识别io事件,放弃CPU控制时间;

    # 一个补丁patch_all,注意要放在所有的import前面,其会将线程、进程替换成gevent框架,使得我们可以用同步编程的方式编写异步代码
    from gevent import monkey;monkey.patch_all()
    import gevent
    import requests
    
    def target0(n):
        print('--start---{}'.format(n))
        res = requests.get('http://www.baidu.com')
        print(res)
        return n
    
    if __name__ == '__main__':
        jobs = [gevent.spawn(target0, 1),gevent.spawn(target0, 2),gevent.spawn(target0, 3)]
        gevent.joinall(jobs)
        print([job.value for job in jobs])
    

    gevent模块分析

    • gevent顶层方法
    gevent.spawn():创建一个普通的Greenlet对象并切换;
    gevent.spawn_later(seconds=3) # 延时创建一个普通的Greenlet对象并切换
    gevent.spawn_raw() # 创建的协程对象属于一个组
    gevent.getcurrent() # 返回当前正在执行的greenlet
    gevent.joinall(jobs):将协程任务添加到事件循环,接收一个任务列表
    gevent.wait() # 可以替代join函数等待循环结束,也可以传入协程对象列表
    gevent.kill() # 杀死一个协程
    gevent.killall() # 杀死一个协程列表里的所有协程
    monkey.patch_all():非常重要,会自动将python的一些标准模块替换成gevent框架
    
    • 设置强制切换的时间
    # 手动设置CPU密集型最大执行时间,如果是单线程的协程不需要关注这个
    sys.setcheckinterval(n):每n条执行尝试进行线程切换,n必须是int
    sys.getswitchinterval() # 默认5ms切换
    
    • Greenlet对象
    # Greenlet对象
    from gevent import Greenlet
    
    # Greenlet对象创建
    job = Greenlet(target0, 3)
    Greenlet.spawn() # 创建一个协程并启动
    Greenlet.spawn_later(seconds=3) # 延时启动
    
    # 协程启动
    job.start() # 将协程加入循环并启动协程
    job.start_later(3) # 延时启动
    
    # 等待任务完成
    job.join() # 等待任务完成
    job.get() # 获取协程返回的值
    
    # 任务中断和判断任务状态
    job.dead() # 判断协程是否死亡
    job.kill() # 杀死正在运行的协程并唤醒其他的协程,这个协程将不会再执行,可以
    job.ready() # 任务完成返回一个真值
    job.successful() # 任务成功完成返回真值,否则抛出错误
    
    # 获取属性
    job.loop # 时间循环对象
    job.value # 获取返回的值
    
    # 捕捉异常
    job.exception # 如果运行有错误,获取它
    job.exc_info # 错误的详细信息
    
    # 设置回调函数
    job.rawlink(back) # 普通回调,将job对象作为回调函数的参数
    job.unlink() # 删除回调函数
    # 执行成功的回调函数
    job.link_value(back)
    # 执行失败的回调函数
    job.link_exception(back)
    

    限制并发

    • 通过设置协程池Pool来限制运行的协程的最大数目。该Pool和multiprocessing进程模块的Pool的API十分相似。

    • Group类管理一组不限制数目的协程对象,但是Pool是它的子类,使用一般用Pool替代。

    def tag():
        print('--start---')
        x = requests.get('http://www.baidu.com')
        print(x)
        print('------end--------')
        return 0
    
    if __name__ == '__main__':
    
        from gevent.pool import Pool
        p = Pool(5)
        for i in range(10):
            p.apply_async(tag)
        p.join()
    
    • gevent.Pool的特殊方法
    pool.wait_available():等待直到有一个协程有结果
    pool.dd(greenlet):向进程池添加一个方法并跟踪,非阻塞
    pool.discard(greenlet):停止跟踪某个协程
    pool.start(greenlet):加入并启动协程
    pool.join():阻塞等待结束
    pool.kill():杀死所有跟踪的协程
    pool.killone(greenlet):杀死一个协程
    
  • 相关阅读:
    ASP.Net MVC开发基础学习笔记:五、区域、模板页与WebAPI初步
    解决编写 xml 没有代码提示
    使用Windows Live Writer拉取之前写的博客
    C# 中的语法糖
    在 visual studio 中添加 ILDASM 工具
    EasyUI 学习笔记
    JQuery 学习笔记--02
    AngularJS 中特性(attr)和属性(prop)的区别
    AngularJS 学习笔记--01
    C# 中常用的索引器
  • 原文地址:https://www.cnblogs.com/cwp-bg/p/9593405.html
Copyright © 2011-2022 走看看