zoukankan      html  css  js  c++  java
  • 简单了解一下事件循环(Event Loop)

    关于我
    一个有思想的程序猿,终身学习实践者,目前在一个创业团队任team lead,技术栈涉及Android、Python、Java和Go,这个也是我们团队的主要技术栈。
    Github:https://github.com/hylinux1024
    微信公众号:终身开发者(angrycode)

    0x00 事件循环(Event Loop)

    在前文《为何你还不懂得如何使用Python协程
    中提到协程是通过asyncio包中的高级API来启动的。而asyncio模块中的核心就是事件循环(Event Loop)。它可用于执行异步任务、事件回调、执行网络IO操作和运行子进程。官方的文档也是建议开发者应该尽量使用asyncio包提供的高级的API,避免直接使用Event Loop对象中的方法。

    系统提供的底层能力的功能模块例如网络连接、文件IO等都会使用到loop

    大多数情况下,这些高级API可以满足众多使用场景,但作为一个有追求的猿类,应该要有一点点探索的精神,看看在asyncio封装之下的Event Loop

    获取Event Loop对象
    • asyncio.get_running_loop()
      获取当前系统线程正在使用的loop对象
    • asyncio.get_event_loop()
      获取当前正在使用的loop对象。如果当前系统线程还没有loop对象,那么就会创建一个新的loop对象,并使用asyncio.set_event_loop(loop)方法设置到当前系统线程中。
    • asyncio.new_event_loop()
      创建一个新的loop对象
    • asyncio.set_event_loop(loop)
      loop设置成系统线程使用的对象

    Event Loop对象的常用方法

    如果使用类似asyncio.run()这些高级API,以下这些方法,基本上很少会用到,建议通读一下,大概知道loop对象拥有哪些API,了解以下底层的实现细节。

    启动和停止
    • loop.run_until_complete(future)
      future对象执行完成才返回
    • loop.run_forever()
      一直运行,直到调用了loop.stop()方法
    • loop.stop()
      停止loop对象
    • loop.is_running()
      判断loop是否正在运行
    • loop.is_closed()
      判断loop是否关闭
    • loop.close()
      关闭loop对象
    • coroutine loop.shutdown_asyncgens()
    try:
        loop.run_forever()
    finally:
        loop.run_until_complete(loop.shutdown_asyncgens())
        loop.close()
    
    回调方法
    • loop.call_soon(callback, *args, context=None)
      在事件循环的下一次迭代中执行callback方法,args是方法中的参数
    • loop.call_soon_threadsafe(callback, *args, context=None)
      线程安全的call_soon()
    iimport asyncio
    import time
    
    def hello_world(loop):
        print('Hello World')
        time.sleep(3)  # 模拟长时间操作
        loop.stop()
    
    loop = asyncio.get_event_loop()
    
    # 使用loop执行 hello_world()
    loop.call_soon(hello_world, loop)
    
    # 会一直阻塞,直到调用了stop方法
    try:
        loop.run_forever()
    finally:
        loop.close()
    
    
    可延迟的回调方法

    可设置延迟执行的方法

    • loop.call_later(delay, callback, *args, context=None)
      延迟delay秒后执行
    • loop.call_at(when, callback, *args, context=None)
      在指定时间点执行
    • loop.time()
      返回当前时间
    import asyncio
    import datetime
    
    
    def display_date(end_time, loop):
        print(datetime.datetime.now())
        if (loop.time() + 1.0) < end_time:
            # 1秒后执行
            loop.call_later(1, display_date, end_time, loop)
        else:
            loop.stop()
    
    loop = asyncio.get_event_loop()
    
    # 执行5秒
    end_time = loop.time() + 5.0
    loop.call_soon(display_date, end_time, loop)
    
    # 一直运行,等待stop的调用
    try:
        loop.run_forever()
    finally:
        loop.close()
    

    执行结果打印5秒时间点

    2019-05-09 22:34:47.885412
    2019-05-09 22:34:48.887513
    2019-05-09 22:34:49.889396
    2019-05-09 22:34:50.894316
    2019-05-09 22:34:51.898457
    
    创建Future和Tasks
    • loop.create_future()
      创建一个绑定事件循环的future对象
    • loop.create_task(coro)
      coro放入调度,并返回task对象
    • loop.set_task_factory(factory)
      设置任务工厂
    • loop.get_task_factory()
      返回任务工厂,有可能返回None
    创建网络连
    coroutine loop.create_connection(protocol_factory, host=None, port=None, *, ssl=None, family=0, proto=0, flags=0, sock=None, local_addr=None, server_hostname=None, ssl_handshake_timeout=None)
    

    指定hostport等参数创建网络连接

    coroutine loop.create_datagram_endpoint(protocol_factory, local_addr=None, remote_addr=None, *, family=0, proto=0, flags=0, reuse_address=None, reuse_port=None, allow_broadcast=None, sock=None)
    

    创建UDP连接

    coroutine loop.create_unix_connection(protocol_factory, path=None, *, ssl=None, sock=None, server_hostname=None, ssl_handshake_timeout=None)
    

    创建Unix连接

    coroutine loop.create_server(protocol_factory, host=None, port=None, *, family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE, sock=None, backlog=100, ssl=None, reuse_address=None, reuse_port=None, ssl_handshake_timeout=None, start_serving=True)
    

    创建TCP服务

    coroutine loop.create_unix_server(protocol_factory, path=None, *, sock=None, backlog=100, ssl=None, ssl_handshake_timeout=None, start_serving=True)
    

    创建Unix服务

    coroutine loop.connect_accepted_socket(protocol_factory, sock, *, ssl=None, ssl_handshake_timeout=None)
    

    封装已建立的连接,返回元组(transport, protocol)

    Event Loop 的实现

    asyncio 的事件循环有两种不同的实现:SelectorEventLoopProactorEventLoop,它们的父类是AbstractEventLoop

    SelectorEventLoop

    这个是asyncio默认使用的Event Loop实现,支持unixwindows平台

    import asyncio
    import selectors
    
    selector = selectors.SelectSelector()
    loop = asyncio.SelectorEventLoop(selector)
    asyncio.set_event_loop(loop)
    
    ProactorEventLoop

    这个是Windows平台专有的实现

    import asyncio
    import sys
    
    if sys.platform == 'win32':
        loop = asyncio.ProactorEventLoop()
        asyncio.set_event_loop(loop)
    

    0x01 总结

    事件循环是asyncio的核心,asncio模块的很多高级接口是通过封装Event Loop对象来实现的。它提供了执行异步任务、事件回调、执行网络IO操作和运行子进程的能力。
    本文是通过官方文档对事件循环的概念和它的常见API做了一个大概的了解。作为《前文》的补充

    0x02 引用

    1. https://docs.python.org/3/library/asyncio-eventloop.html
  • 相关阅读:
    STL之map UVa156
    STL之vector UVa101
    STL之set UVa10815
    无修改区间查询 BNU Can you answer these queries I
    区间修改点查询 HDU1556
    无废话ExtJs 入门教程九[数字字段:NumberField、隐藏字段Hidden、日期字段:DataFiedl]
    无废话ExtJs 入门教程七[登陆窗体Demo:Login]
    无废话ExtJs 入门教程六[按钮:Button]
    无废话ExtJs 入门教程五[文本框:TextField]
    无废话ExtJs 入门教程四[表单:FormPanel]
  • 原文地址:https://www.cnblogs.com/angrycode/p/11387024.html
Copyright © 2011-2022 走看看