美文网首页PythonPython学习
Python并发编程——协程

Python并发编程——协程

作者: xiaogp | 来源:发表于2021-05-16 20:29 被阅读0次

    摘要:Python协程gevent

    协程基本概念

    协程,又称微线程,纤程。英文名Coroutine,是Python个中另外一种实现多任务的方式,只不过比线程更小占用更小执行单元。 它自带CPU上下文。这样只要在合适的时机, 可以把一个协程切换到另一个协程。,切换的次数以及什么时候再切换由开发者自己确定

    (1)协程和线程差异

    在实现多任务时, 线程切换从系统层面远不止保存和恢复 CPU上下文这么简单。 操作系统为了程序运行的高效性每个线程都有自己缓存的数据,操作系统还会帮你做这些数据的恢复操作。 所以线程的切换非常耗性能。但是协程的切换只是单纯的操作CPU的上下文,所以一秒钟切换个上百万次系统都抗的住,简单而言协程的特点是

    • 线程之间需要上下文切换成本相对协程来说是比较高的,尤其在开启线程较多时,但协程的切换成本非常低。
    • 同样的线程的切换更多的是靠操作系统来控制,而协程的执行由开发者自己控制
    (2)协程对比进程线程
    • 协程用户层面的概念,是由用户通过代码自定义的
    • 进程线程都是操作系统层面的概念,由计算机自己实现

    Python中协程的实现

    主要整理Python中使用gevent实现协程的代码实现,gevent是一个基于协程的Python网络库,在遇到IO阻塞时,程序会自动进行切换,可以让开发者用同步的方式写异步IO代码

    (1)gevent的基本使用

    gevent通过gevent.spawn创建协程任务,传入执行函数,可以给执行函数传入多个参数,使用joinall启动任务且主进程等待协程执行完毕退出

    import time
    import datetime
    
    import gevent
    
    
    def my_func(sid, sid2):
        time.sleep(3)
        print("sid:{}, sid2:{}, time:{}".format(sid, sid2, datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")))
    
    
    t1 = gevent.spawn(my_func, sid="a", sid2="b")  # t1 = gevent.spawn(my_func, "a", "b")
    t2 = gevent.spawn(my_func, sid="a", sid2="b")
    gevent.joinall([t1, t2])
    

    输出如下,没有达到并行效果,还是顺序执行,需要增加gevent.monkey补丁

    sid:a, sid2:b, time:2021-05-16 20:10:51
    sid:a, sid2:b, time:2021-05-16 20:10:54
    

    加入补丁如下

    import time
    import datetime
    
    import gevent
    from gevent import monkey
    monkey.patch_all()
    
    
    def my_func(sid, sid2):
        time.sleep(3)
        print("sid:{}, sid2:{}, time:{}".format(sid, sid2, datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")))
    
    
    t1 = gevent.spawn(my_func, sid="a", sid2="b")
    t2 = gevent.spawn(my_func, sid="a", sid2="b")
    gevent.joinall([t1, t2])
    

    输出如下

    sid:a, sid2:b, time:2021-05-16 20:14:19
    sid:a, sid2:b, time:2021-05-16 20:14:19
    

    (2)协程池的使用

    协程池相对要好用些,可以自定义一个协程池指定个数,把待执行的列表丢进去,即可执行

    import time
    import datetime
    
    import gevent
    from gevent import monkey
    from gevent.pool import Pool
    monkey.patch_all()
    
    
    def my_func(sid, sid2):
        time.sleep(3)
        print("sid:{}, sid2:{}, time:{}".format(sid, sid2, datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")))
    
    
    if __name__ == '__main__':
        pool = Pool(4)
        jobs = [["a", "b"], ["b", "c"], ["c", "d"], ["d", "e"]]
        for job in jobs:
            pool.apply_async(my_func, job)
        pool.join()
    

    如果函数有输出值,则可以使用map获得协程池的执行结果,gevent的协程池不支持map传入多个执行函数的参数,可以在执行函数中解包如下

    import time
    import datetime
    
    import gevent
    from gevent import monkey
    from gevent.pool import Pool
    monkey.patch_all()
    
    
    def my_func(sid: tuple):
        sid1 = sid[0]
        sid2 = sid[1]
        time.sleep(3)
        print("sid:{}, sid2:{}, time:{}".format(sid1, sid2, datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")))
        return str(sid1) + str(sid2)
    
    
    if __name__ == '__main__':
        pool = Pool(4)
        jobs = [("a", "b"), ("b", "c"), ("c", "d"), ("d", "e")]
        res = pool.map(my_func, jobs)
        pool.join()
        print(res)
    

    输出如下

    sid:a, sid2:b, time:2021-05-16 22:09:40
    sid:b, sid2:c, time:2021-05-16 22:09:40
    sid:c, sid2:d, time:2021-05-16 22:09:40
    sid:d, sid2:e, time:2021-05-16 22:09:40
    ['ab', 'bc', 'cd', 'de']
    

    相关文章

      网友评论

        本文标题:Python并发编程——协程

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