ITPub博客

首页 > 数据库 > 数据库开发技术 > 深入FastAPI异步编程-之并发编程(6)~事件循环

深入FastAPI异步编程-之并发编程(6)~事件循环

原创 数据库开发技术 作者:程序员小钟同学 时间:2024-01-16 00:51:08 0 删除 编辑

前言

前面我们一直使用到关于事件循环,我们也深知事件循环是asyncio库的核心机制,它是实现我们的实现代码以非阻塞的方式执行,从而可以同时处理多个异步操作的关键,且需要注意,通常我们的事件循环都是在一个单线程内运行的。

可能我们有疑问了,为什么我们单线程内也可以实现并发任务?不应该所有的任务都是串行的吗?所以这也是我们使用异步的关键点之一。

在Python的asyncio库中,事尽管我们在单线程的执行所有的异步操作和任务,但是基于协程和事件循环的机制,就非常巧妙的实现了非阻塞的操作,允许在单个线程中并行处理多个任务。asyncio库中实现并发机制是通过协作式多任务和事件驱动编程来实现的,而不是通过线程之间的并行执行。

在事件循环中,我们的‘任务’(包括协程和任务等)是按照它们被添加到事件循环的顺序执行,但是我们的每个任务在执行过程期间如果遇到IO阻塞,如果一个任务在进行I/O操作,事件循环可以切换到执行其他任务,直到I/O操作完成。当I/O操作返回结果时,事件循环会处理这个事件,并恢复执行之前被挂起的任务。这样就可以更高效的实现任务的并发处理。

因此我们的事件循环可以同时管理多个协程和任务,而不需要创建多个线程或进程来进行处理。

首先我们需要了解一下,事件循环的主要职责包括:

  • 调度和运行协程。
  • 管理定时器和回调函数。
  • 处理I/O事件和信号。
  • 协调任务之间的执行顺序。

在前面其实我们很少直接和我们的事件循环打交道,多数的情况下,我们都有对应封装好的函数可以直接调用,常见的有:

  • loop.create_task() 用于将协程任务注册到指定的事件循环中。

  • asyncio.get_event_loop():获取当前事件循环。

  • asyncio.run():启动事件循环并运行一个协程或函数。这个函数会创建一个事件循环,运行指定的main协程或函数,并在完成后关闭事件循环。通常不需要手动获取事件循环,因为asyncio.run()函数会为你创建并管理事件循环。

  • loop.create_task(coro):创建一个任务并将其添加到事件循环中。这个方法会返回一个任务对象,可以用来跟踪协程的执行。

  • loop.run_until_complete(future):运行事件循环,直到指定的异步事件(通常是一个任务或一个Future对象)完成。

  • loop.run_in_executor(executor, func, *args, **kwargs):在指定的线程池执行器中运行一个函数。这个方法允许你在事件循环外部执行阻塞操作,而不会阻塞事件循环的执行。loop.run_in_executor 返回一个 Future 对象,我们可以使用 await 关键字来等待该 Future 对象的完成并获取它的返回值。由于该函数是异步的,因此它不会阻塞当前线程的执行。

  • loop.call_soon(func, *args, **kwargs):在事件循环的下一个迭代中调用一个函数。

  • loop.call_later(delay, func, *args, **kwargs):在指定的延迟后调用一个函数。

  • loop.call_at(when, func, *args, **kwargs):在指定的时间点调用一个函数。

  • loop.stop()、loop.close():关闭事件循环。

  • loop.run_forever()一般会结合loop.stop()一起进行使用。

下面我们会尝试去理解和应用上面一些常见的方法。

asyncio.run()自动创建事件循环

我们知道通常我们的运行一个协程使用的是上面的方法才可以。似乎我们的整个过程都没手动的进行过相关进行手动的创建我们的事件循环对象,原因是:asyncio.run()内部以及自动的帮我们创建好了。也就是说当我们使用asyncio.run()一个协程或任务的时候:

  • asyncio.run() 会自己创建一个 EventLoop 实例,它开始负责协调异步任务、定时器、协程等的执行。当我们的 asyncio.run() 被调用时,它会尝试获取当前线程的 EventLoop,如果没有则创建一个新的。

  • 设置事件循环:在获取或创建 EventLoop 后,我们的asyncio.run()内部会把这个EventLoop事件循环设置为当前线程的事件循环。此时此刻,也意味着在这个线程中所有的 async 函数都会被这个事件循环管理。

  • 执行主协程:当钓鱼asyncio.run() 时,系统会将会执行一个被称为“主协程”的 async 函数。主协程是异步应用的入口点,通常包含应用程序的主要逻辑。通过 asyncio.run() 传入的协程函数在这个阶段被执行。

  • 等待主协程完成:asyncio.run() 会等待主协程执行完成。在主协程执行完毕后,事件循环会继续运行,直到没有更多的任务需要执行。

  • 关闭事件循环:当主协程执行完毕后,asyncio.run() 会关闭事件循环,释放资源。这通常意味着所有的事件循环内部资源,包括如网络连接和定时器等等,都会被清理。

  • 返回结果:asyncio.run() 在关闭事件循环后会返回主协程的结果。如果主协程是一个 Task 对象,这个结果将是 Task 的结果。如果没有设置返回值,则返回 None。

如下示例:

import asyncio
async def ss():
    return "我是小钟同学"
print(asyncio.run(ss()))

输出结果为:

我是小钟同学

说明了我们的主协程返回值就是协程最终的返回值。还是需要再强调一次:为了保证 asyncio 的线程安全,asyncio.run() 在执行过程中会确保只有一个线程能够访问事件循环,这也意味着在多线程环境中 asyncio.run() 只能调用一次。如果在同一个线程中多次调用 asyncio.run(),后续的调用将会抛出 RuntimeError。如下示例代码:

import asyncio
async def ss():
    return "我是小钟同学"


async def main():
    print("main")
    print(asyncio.run(ss()))
    print("main end")

print(asyncio.run(main()))

运行会会得到如下错误结果:

Traceback (most recent call last):
  File "D:\code_loacl\mm_ring_v2\ss.py", line 11, in <module>
    print(asyncio.run(main()))
          ^^^^^^^^^^^^^^^^^^^
  File "D:\Program Files\Python311\Lib\asyncio\runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "D:\Program Files\Python311\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\Program Files\Python311\Lib\asyncio\base_events.py", line 653, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "D:\code_loacl\mm_ring_v2\ss.py", line 8, in main
    print(asyncio.run(ss()))
          ^^^^^^^^^^^^^^^^^
  File "D:\Program Files\Python311\Lib\asyncio\runners.py", line 186, in run
    raise RuntimeError(
RuntimeError: asyncio.run() cannot be called from a running event loop
sys:1: RuntimeWarning: coroutine 'ss' was never awaited

在上面我们都是直接的使用封装好的创建事件对象,当然我们也可以手动的创建,如下示例代码所示:

import asyncio
async def main():
    print("我是谁")
    await asyncio.sleep(1)
    print("我是同学")
# 创建事件循环
loop = asyncio.get_event_loop()
# 运行事件循环
try:
    loop.run_until_complete(main())
except KeyboardInterrupt:
    print("程序被终止")
finally:
    loop.close()

需注意的是,如果我们手动的创建事件循环的话,则需要注意:

  • 事件件循环的创建和关闭通常是在应用程序的最开始和最末尾进行。
  • 需要留意在合适的时机使用await关键字,以便在异步任务之间进行等待。

协程异常的捕获

示例1:在事件循环处进行在协程内部使用try/except

import asyncio

async def my_coroutine():
    # 异步任务的具体实现
    await asyncio.sleep(1)
    raise Exception("协程内部出现异常")

loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(my_coroutine())
except Exception as e:
    print("捕获到异常:", e)
    loop.stop()
finally:
    loop.close()

示例2:在协程内部使用try/except

import asyncio

async def my_coroutine():
    try:
        # 协程任务的具体实现
        await asyncio.sleep(1)
        raise ValueError("协程内部出现异常")
    except ValueError as e:
        print("捕获到异常:", e)

loop = asyncio.get_event_loop()
loop.run_until_complete(my_coroutine())

示例3:使用asyncio.gather()来集合多个协程的执行,并使用return_exceptions=True参数来捕获所有异常.

import asyncio

async def my_coroutine():
    # 协程任务的具体实现
    await asyncio.sleep(1)
    raise ValueError("协程内部出现异常")

async def main():
    coroutines = [my_coroutine(), my_coroutine()]
    try:
        results = await asyncio.gather(*coroutines, return_exceptions=True)
        for result in results:
            if isinstance(result, Exception):
                print("捕获到异常:", result)
    except Exception as e:
        print("捕获到异常:", e)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

loop.create_task() 用于将协程任务注册到指定的事件循环中。

loop.create_task(coro)主要是一个用于将协程任务注册到指定的事件循环中的方法。loop 表示要注册任务的事件循环对象,coro 则是一个协程函数或协程对象。该方法会返回一个 Task 对象,表示被创建的任务。任务会在事件循环中被调度执行。

主要应用:

  • 注册和调度协程任务:将我们的协程或任务注册到指定的事件循环中。被注册的任务将会被事件循环调度执行,从而实现并发和异步相关的操作。

  • 并发执行多个任务:我们可以在同一个事件循环中注册多个协程任务,并发地执行它们。

  • 控制任务的执行顺序和优先级:通过适当地安排和调度任务的注册顺序,可以控制任务的执行顺序和优先级。

  • 处理任务的结果和异常:通过获取 Task 对象并监视其状态,可以处理任务的执行结果和异常。

并发执行多任务的应用:

import asyncio

# 在第一个事件循环中运行任务
async def task1():
    while True:
        print("Task 1")
        await asyncio.sleep(1)

# 在第二个事件循环中运行任务
async def task2():
    while True:
        print("Task 2")
        await asyncio.sleep(2)

# 创建并运行多个独立的事件循环
async def main():
    # 创建第一个事件循环并注册任务
    loop1 = asyncio.get_running_loop()
    loop1.create_task(task1())
    loop1.create_task(task2())

    # 运行事件循环
    await asyncio.gather(*asyncio.all_tasks())

# 启动程序
asyncio.run(main())

上面程序的运行结果如下:

Task 1 <ProactorEventLoop running=True closed=False debug=False> 1297158880336
Task 2 <ProactorEventLoop running=True closed=False debug=False> 1297158880336
Task 1 <ProactorEventLoop running=True closed=False debug=False> 1297158880336
Task 2 <ProactorEventLoop running=True closed=False debug=False> 1297158880336
Task 1 <ProactorEventLoop running=True closed=False debug=False> 1297158880336
Task 1 <ProactorEventLoop running=True closed=False debug=False> 1297158880336
Task 2 <ProactorEventLoop running=True closed=False debug=False> 1297158880336
Task 1 <ProactorEventLoop running=True closed=False debug=False> 1297158880336
Task 1 <ProactorEventLoop running=True closed=False debug=False> 1297158880336

上面的程序我们的是使用的是同一事件循环,加入我们需要不同的事件循环呢?也就是不同的线程使用不同的事件循环?如下示例代码所示:

import asyncio
import threading

# 创建第一个事件循环
loop1 = asyncio.new_event_loop()

# 创建第二个事件循环
loop2 = asyncio.new_event_loop()

# 在第一个事件循环中运行任务
async def task1():
    while True:
        print("Task 1",asyncio.get_running_loop(),id(asyncio.get_running_loop()))
        await asyncio.sleep(1)

loop1.create_task(task1())

# 在第二个事件循环中运行任务
async def task2():
    while True:
        print("Task 2",asyncio.get_running_loop(),id(asyncio.get_running_loop()))
        await asyncio.sleep(2)

loop2.create_task(task2())

# 启动两个事件循环
thread1 = threading.Thread(target=loop1.run_forever)
thread1.start()

thread2 = threading.Thread(target=loop2.run_forever)
thread2.start()

输出结果如下:

Task 1 <ProactorEventLoop running=True closed=False debug=False> 1682941131408
Task 2 <ProactorEventLoop running=True closed=False debug=False> 1682941165840
Task 1 <ProactorEventLoop running=True closed=False debug=False> 1682941131408
Task 2 <ProactorEventLoop running=True closed=False debug=False> 1682941165840
Task 1 <ProactorEventLoop running=True closed=False debug=False> 1682941131408
Task 1 <ProactorEventLoop running=True closed=False debug=False> 1682941131408

从输出结果可以得知我们的事件循环的对象不同了!!!

ProactorEventLoop 是 Python 标准库 asyncio 模块提供的一种事件循环实现方式,它是基于 Windows IOCP (I/O Completion Ports) 的事件循环。后续会针对这个事件循环展开说明。

loop.run_in_executor 指定的线程池执行器中运行阻塞函数

在前面可知,我们的loop.run_in_executor 可以在指定的线程池执行器中运行一个函数。这个方法允许你在事件循环外部执行阻塞操作,而不会阻塞事件循环的执行。也就是即使我们的在单线程内跑阻塞的任务,也可以把阻塞的任务进行异步化处理。如下示例代码所示:

import asyncio
import time
import requests

def blocking_sync_func(arg):
    # 阻塞的同步函数,模拟耗时操作
    resylt = requests.get('https://www.baidu.com')
    return resylt.status_code

async def main():
    loop = asyncio.get_event_loop()

    start_time = time.time()
    print("开始时间",start_time)
    tasks = [loop.run_in_executor(None, blocking_sync_func, i) for i in range(100)]
    results = await asyncio.gather(*tasks)
    end_time = time.time()
    print("结束时间",end_time)
    print(f"总共用时:{end_time-start_time:.2f}s")
    print(f"结果列表:{results}")

asyncio.run(main())

在上面的代码中,我们定义了一个阻塞的同步函数blocking_sync_func(),它里面并且使用requests进行阻塞任务的执行,并返回请求处理结果状态。在main()协程中,我们使用loop.run_in_executor()将这个同步函数转为异步操作,并把他们都放在后台线程中执行,且我们循环调用blocking_sync_func()10次。

前面我们知道loop.run_in_executor()返回一个 Future 对象,我们可以使用 await 关键字来等待该 Future 对象的完成并获取它的返回值,并使用asyncio.gather()等待所有异步任务完成。由于所有函数都是异步的,因此它不会阻塞当前线程的执行。由于使用了loop.run_in_executor(),所以我们可以同时运行多个阻塞的同步函数,而不会阻塞事件循环。

上面示例输出的结果如下:

开始时间 1705136380.8293176
结束时间 1705136386.6214948
总共用时:5.79s
结果列表:[200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200]

loop.call_soon()

这个方法用于安排一个回调函数在事件循环中【尽快被执行】。它接受一个可调用对象作为参数,并将其放入事件循环的待执行队列中。当事件循环空闲时,会立即执行这个回调函数。使用 loop.call_soon() 可以实现一些简单的异步操作

但是需要注意回调函数的执行时间,避免阻塞事件循环。如果回调函数需要执行一些耗时的操作,可以考虑使用 loop.run_in_executor() 将这些操作放到线程池中执行,避免阻塞事件循环。

如下一个示例,:

import asyncio
import time


def sync_func():
    print("开始执行同步函数")
    time.sleep(5)
    print("同步函数执行完毕")

async def main():
    loop = asyncio.get_event_loop()

    # 使用 loop.call_soon() 安排同步函数的执行
    loop.call_soon(loop.run_in_executor, None, sync_func)

    # 运行事件循环直到完成指定的任务
    await asyncio.sleep(1)
    loop.stop()

# 运行主协程
asyncio.run(main())

运行代码会会提示如下的错误:

开始执行同步函数
Traceback (most recent call last):
  File "D:\code_loacl\mm_ring_v2\ss222333333344444.py", line 21, in <module>
    asyncio.run(main())
  File "D:\Program Files\Python311\Lib\asyncio\runners.py", line 189, in run
    with Runner(debug=debug) as runner:
  File "D:\Program Files\Python311\Lib\asyncio\runners.py", line 63, in __exit__
    self.close()
  File "D:\Program Files\Python311\Lib\asyncio\runners.py", line 73, in close
    loop.run_until_complete(loop.shutdown_default_executor())
  File "D:\Program Files\Python311\Lib\asyncio\base_events.py", line 651, in run_until_complete
    raise RuntimeError('Event loop stopped before Future completed.')
RuntimeError: Event loop stopped before Future completed.
同步函数执行完毕

上面可以看出我们的逻辑处理,由于在事件循环结束前,也就是我们的await asyncio.sleep(1)之后,就停止了事件循环,但是由于我们的安排的任务sync_func还没有被完成或取消,因此抛出了 RuntimeError。

需要注意,我们的loop.call_soon() 可以安排执行同步的任务也可以安排异步的任务,如下的示例:

import asyncio
import time


def callback_with_args(arg1, arg2):
    time.sleep(1)
    print(f"同步回答的参数: {arg1}, {arg2}")

async def main():
    loop = asyncio.get_event_loop()

    # 使用 loop.call_soon() 安排带有参数的回调函数的执行
    loop.call_soon(callback_with_args, "1", "2")

    # 定义一个异步任务
    async def my_task():
        await asyncio.sleep(2)
        print("异步任务完成")

    # 使用 loop.call_soon() 安排异步任务的执行
    loop.call_soon(asyncio.create_task, my_task())

    # 运行事件循环直到完成指定的任务
    await asyncio.sleep(3)
    loop.stop()

# 运行主协程
asyncio.run(main())

附加说明:loop.call_soon_threadsafe(callback, *args, context=None)是call_soon()函数的线程安全版本,计划回调函数必须在另一个线程中使用。

loop.call_soon_threadsafe() 方法通常在以下情况下使用:

  • 在不同的线程中调度任务:如果你有一个异步任务在一个线程中执行,但是需要在另一个线程中将其添加到事件循环中执行,就可以使用 loop.call_soon_threadsafe() 方法。这种情况通常发生在你希望在主线程之外执行一些阻塞操作,并在其完成后将结果返回给主线程的情况下。如下示例代码:
import asyncio
import threading



def blocking_task():
    """
    阻塞任务,会等待2秒钟然后返回"
后台任务完成"
    "
""
    import time
    time.sleep(2)
    return "后台任务完成"

def run_blocking_task(loop, future):
    """
    在主线程之外创建一个新线程来执行阻塞任务
    "
""
    result = blocking_task()
    loop.call_soon_threadsafe(future.set_result, result)

def main():
    """
    主函数
    "
""
    # 创建事件循环
    loop = asyncio.get_event_loop()

    # 创建一个Future对象
    future = asyncio.Future()

    # 在主线程之外创建一个新线程来执行阻塞任务
    thread = threading.Thread(target=run_blocking_task, args=(loop, future))
    thread.start()

    # 在主线程中执行异步任务
    async def run_async_task():
        awaitable = asyncio.wrap_future(future)
        await asyncio.ensure_future(awaitable)

    loop.run_until_complete(run_async_task())

    # 获取阻塞任务的结果
    loop.run_until_complete(future)
    blocking_result = future.result()
    print('后台任务的结果',blocking_result)

    # 关闭事件循环
    loop.close()

if __name__ == "__main__":
    main()


在上面的代码中我们

  • 首先定义了def blocking_task()函数,该函数是一个阻塞任务,使用time.sleep(2)让当前线程暂停2秒,然后返回"后台任务完成"。
  • 为了能在异步环境中处理阻塞任务的结果,定义了def run_blocking_task(loop, future)函数,它会在新的线程中执行阻塞任务,并通过调用loop.call_soon_threadsafe(future.set_result, result)将阻塞任务的结果安全地传递给主线程中的Future对象。

在main()函数中我们整个的流程处理如下:


  1. 获取事件循环(event loop):loop = asyncio.get_event_loop()。

    1. 创建一个Future对象future = asyncio.Future(),用于存储阻塞任务的结果。

    1. 启动一个新的线程来执行阻塞任务,并将事件循环和Future对象作为参数传入:thread = threading.Thread(target=run_blocking_task, args=(loop, future)),并启动线程thread.start()。

    1. 定义一个异步协程async def run_async_task(),在这个协程中,通过awaitable = asyncio.wrap_future(future)将Future对象包装为可被异步等待的对象,然后使用asyncio.ensure_future(awaitable)安排这个异步操作到事件循环中执行。
  • 5 ) 使用事件循环运行异步协程直到完成:loop.run_until_complete(run_async_task())。此时,主线程不会被阻塞任务阻碍,而是同步等待异步任务完成。
  • 6 ) 再次调用loop.run_until_complete(future)以确保阻塞任务的结果已经存入Future对象,并通过blocking_result = future.result()获取阻塞任务的实际结果。
  • 7 ) 最后,关闭事件循环:loop.close()。
  • loop.call_later()

    loop.call_later() 用于在指定时间后调度一个回调函数的执行。它接受两个参数:delay 和 callback。

    • delay: 表示延迟的时间,可以是一个整数或浮点数,它的单位是秒。
    • callback: 是一个可调用对象,表示要执行的回调函数。(可以是任务)

    示例代码如下:

    import asyncio
    import functools

    async def light_candles():
        print("light_candles...开始")
        await asyncio.sleep(3)  # 模拟异步操作
        print("light_candles!!!结束")


    def callback():
        print('Callback function called')

    async def main():
        loop = asyncio.get_running_loop()
        loop.call_later(2, callback)  # 延迟 2 秒后调用 callback 函数
        loop.call_later(3, asyncio.create_task, light_candles())

        print('为了看到延迟后执行的效果!!!!')
        await asyncio.sleep(6)
        print('延迟效果结束')

    asyncio.run(main())

    输出的结果如下:

    为了看到延迟后执行的效果!!!!
    Callback function called
    light_candles...开始
    延迟效果结束
    light_candles!!!结束

    修改我们的light_candles()里面的休眠时间为4或更长的时候,则会输出:

    为了看到延迟后执行的效果!!!!
    Callback function called
    light_candles...开始
    延迟效果结束

    由此可见,loop.call_later() 并不是一个精确的定时器,它只是在指定的时间后调度回调函数的执行。实际的延迟时间可能会受到事件循环的负载和其他因素的影响,被调度的任务如果太久,有可能也不会正常被执行!

    结合FastAPI我们可以使用它做类似异步的后台的任务的功能哟@@@@@如下示例代码所示:

    import asyncio

    import uvicorn
    from fastapi import FastAPI

    app = FastAPI()


    @app.get("/delayed_task")
    async def delayed_task():
        loop = asyncio.get_running_loop()

        async def task():
            await asyncio.sleep(1)
            print('延迟的任务异步的执行了!!!!!')
            return {"message": "Delayed task executed"}

        def callback():
            asyncio.create_task(task())

        loop.call_later(1, callback)

        return {"message": f"延迟任务的执行演示"}

    上面我们请求接口会立即完成我们的接口的请求回应,但是大约两秒后就会输出的我们的结果‘延迟的任务异步的执行了!!!!’!

    loop.call_at()

    loop.call_at() 用于在指定的时间点调度执行某个回调函数。

    • when:一个浮点数或整数,表示要调度执行回调函数的时间点。它是相对于事件循环运行时间的秒数。例如,when=loop.time() + 10 表示在当前时间的 10 秒后执行回调函数。
    • callback:要调度执行的回调函数。
    • *args:回调函数的参数。

    示例应用:

    import asyncio
    import time

    import uvicorn
    from fastapi import FastAPI

    app = FastAPI()


    def async_task():
        time.sleep(2)
        print("异步定时任务执行结束")

    def schedule_task():
        loop = asyncio.get_event_loop()
        when = loop.time() + 1
        loop.call_at(when, async_task)

    @app.get("/schedule")
    async def schedule():
        schedule_task()
        return {"message": "Task scheduled"}


    if __name__ == "__main__":
        uvicorn.run(app, host="127.0.0.1", port=8050)

    注意上面的代码,我在延迟函数的调用是阻塞了!!!!time.sleep(2),你可以自己体验是什么感觉!!!

    asyncio.new_event_loop()和set_event_loop()

    asyncio.new_event_loop()用于创建一个新的事件循环(event loop)对象,并返回该对象。通常会结合我们的 set_event_loop()来一起使用。如下示例:

    import asyncio

    async def my_task():
        print("Running my task")

    # 创建事件循环对象
    loop = asyncio.get_event_loop()

    # 设置默认事件循环
    asyncio.set_event_loop(loop)

    # 注册任务到默认事件循环并运行
    loop.run_until_complete(my_task())

    文笔有限,如有笔误或错误!欢迎批评指正!感谢各位大佬!有什么问题也可以随时交流!

    结尾

    END

    简书:https://www.jianshu.com/u/d6960089b087

    掘金:https://juejin.cn/user/2963939079225608

    公众号:微信搜【程序员小钟同学】

    新开QQ群号,欢迎随时加群交流,相互学习。QQ群号:247491107

    小钟同学 | 文 【欢迎一起学习交流】| QQ:308711822


    原文作者:程序员小钟同学;如有侵权,请联系管理员删除。

    请登录后发表评论 登录
    全部评论

    注册时间:2021-02-20

    • 博文量
      224
    • 访问量
      111682