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
  • 相关阅读:
    结合中断上下文切换和进程上下文切换分析Linux内核的一般执行过程--课程实验3
    深入理解系统调用 -- 课程实验2
    基于mykernel 2.0编写一个操作系统内核--课程实验1
    如何评测软件工程知识技能水平?
    如何评测一个软件工程师的计算机网络知识水平与网络编程技能水平?
    深入理解TCP协议及其源代码
    Socket与系统调用深度分析
    创新产品的需求分析:未来的图书是什么样的
    构建调试Linux内核网络代码的环境MenuOS系统
    php db2 返回当前insert记录的自增id
  • 原文地址:https://www.cnblogs.com/angrycode/p/11387024.html
Copyright © 2011-2022 走看看