理解Python异步中的事件循环(asyncio管理事件循环)

  • Python中的异步编程

    Python实现异步编程的方案有:

    • twisted:使用事件驱动机制来提升python性能
    • gevent:使用greenlet在用户态完成栈和上下文切换来减少切换带来的性能损耗
    • tornado:使用生成器来保存协程上下文及状态,使用原生的python语法实现了协程
    • asyncio:异步编程标准化。
  • Python异步编程的特点

    • 单线程、异步阻塞
    • 异步的实现基于协程,不仅使得Python中的异步是阻塞的,而且最小执行单位不再是单个任务,而是每个任务的一部分。
  • Python高并发方案的区别

    多进程、多线程这类方案,IO的调度取决于系统;

    协程,调度来自于用户。

    asyncio模块实现异步,也是采用协程实现。

  • asyncio模块的主要组件和概念

    • 事件循环:asyncio模块中,每一个进程都有一个事件循环。程序员把一些函数注册到事件循环上,当满足事件发生的时候,调用相应的协程函数;
    • 协程:子程序的泛化概念,可以在执行期间暂停,等待外部的处理完成之后,从之前暂停的地方回复执行。理解为一个使用async关键字定义的函数。它的调用不会立即执行,而是会返回一个协程对象,协程对象需要注册到事件循环,由事件循环调用
    • Futures:定义Future对象,表示尚未完成的计算;
    • Tasks:用于封装和管理并行模式下的协程;协程对象是一个可以挂起的函数,任务则是对协程进一步封装,其中包含任务的各种状态;
    • async/await关键字:定义协程的关键字,async定义一个协程,await用于挂起阻塞的异步调用接口。
  • 事件循环Event loop(维基百科

    事件的本质就是异步。

    事件源:可以产生事件的实体;

    事件处理者:能处理事件的实体;

    事件循环:第三方实体。

    事件循环的作用是管理所有的事件,在整个程序运行过程中不断循环执行,追踪事件发生的顺序将它们放到队列中,当主线程空闲的时候,调用相应的事件处理者处理事件。

  • 协程与事件循环案例

    import asyncio
    import time
    
    async def do_some_work(x):  # async关键字定义一个协程
        print('Waiting:', x) 
    coroutine = do_some_work(2)   # 协程的调用不会直接执行,而是返回一个协程对象
    
    loop = asyncio.get_event_loop()  # 创建一个事件循环
    loop.run_until_complete(coroutine) # run_until_complete将协程注册到事件循环,并启动事件循环。
    
    

    协程对象不能直接运行,在注册事件循环时,run_until_complete将协程包装成了一个tasktask对象是Futurn类的子类,保存了协程运行后的状态,用于未来获取协程的结果。

    import asyncio
    import time
    
    async def do_some_work(x):  # async关键字定义一个协程
        print('Waiting:', x) 
    coroutine = do_some_work(2)   # 协程的调用不会直接执行,而是返回一个协程对象
    
    loop = asyncio.get_event_loop()  # 创建一个事件循环
    
    task = loop.create_task(coroutine) # 创建task,此时的task尚未加入事件循环,状态为pending
    loop.run_until_complete(task) # run_until_complete将task注册到事件循环,并启动事件循环。
    # task执行完后,状态为finished
    
    

    run_until_complete参数是一个future对象,当传入的是:

    • 协程,会自动封装成task
    • tasktaskFuture的子类。asyncio.ensure_future(coroutine)loop.create_task(coroutine)都可以创建task
    # 绑定回调函数
    import asyncio
    import time
    
    async def do_some_work(x):  # async关键字定义一个协程
        print('Waiting:', x) 
        return 'Done after {}s'.format(x)  # 返回值
    def callback(future): # 定义一个回调函数,最后一个参数是future对象,如果这里有多个参数,下方通过偏函数导入
        print('Callback: ', future.result()) # 返回future的结果
        
    coroutine = do_some_work(2)   # 协程的调用不会直接执行,而是返回一个协程对象
    
    loop = asyncio.get_event_loop()  # 创建一个事件循环
    
    task = loop.create_task(coroutine) # 创建task,此时的task尚未加入事件循环,状态为pending
    task.add_done_callback(callback) # 绑定回调函数,在task执行完毕的时候获取执行的结果
    loop.run_until_complete(task) # run_until_complete将task注册到事件循环,并启动事件循环。
    # task执行完后,状态为finished
    
    

    coroutine执行结束时会调用回调函数,并通过参数future获取协程执行的结果,此future是没有传入的,实际上与创建的task是同一个对象

    # 阻塞和await
    import asyncio
    import time
    
    now = lambda: time.time()
    
    async def do_some_work(x):
        print('Waiting: ', x) 
        await asyncio.sleep(x) # 执行sleep时,await使此协程主动让出控制权,loop调用其他协程
        return 'Done after {}s'.format(x)
    
    start = now()
    
    coroutine = do_some_work(2)
    loop = asyncio.get_event_loop()
    task = asyncio.ensure_future(coroutine)
    loop.run_until_complete(task)
    
    print('Task ret: ', task.result())
    print('TIME: ', now() - start)  
    # 以上还只有一个协程,让出控制权也暂时无用,如果有多个协程,协程A让出控制权,loop就会把控制权分配给协程B,实现了并发。
    # asyncio实现并发:每当一个协程任务阻塞的时候就await,让后其他协程继续工作。
    import asyncio
    
    import time
    
    now = lambda: time.time()
    
    async def do_some_work(x):
        print('Waiting: ', x)
    
        await asyncio.sleep(x)
        return 'Done after {}s'.format(x)
    
    start = now()
    
    coroutine1 = do_some_work(1)
    coroutine2 = do_some_work(2)
    coroutine3 = do_some_work(4)
    
    tasks = [
        asyncio.ensure_future(coroutine1),
        asyncio.ensure_future(coroutine2),
        asyncio.ensure_future(coroutine3)
    ]
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))   # asyncio.wait(接收task列表);asyncio.gather(接收多个task)
    
    for task in tasks:
        print('Task ret: ', task.result())
    
    print('TIME: ', now() - start)
    
  • How the heck does async/await work in Python 3.5?

    From Wikipedia, an eventloop is a programming construct that waits for and dispathches enents or messages in a program.Basically an event loop lets you go, “When A happens, do B”.

    In Python’s case,asyncio was added to the standard library to provide an event loop.

  • Reference

  1. 线程、事件与QObject
  2. Microsoft Windows的消息循环
  3. python协程进阶,原来实现一个事件循环可以如此简单!
  4. Python并行编程 中文版
  5. Python 协程和事件循环
  6. 从0到1,Python异步编程的演进之路
  7. 11.6. asyncio — 异步 I/O,事件循环和并发工具
  8. Python黑魔法 — 异步IO( asyncio) 协程
  9. How the heck does async/await work in Python 3.5?

猜你喜欢

转载自blog.csdn.net/The_Time_Runner/article/details/105801342