
<p>本文旨在解决在 Python 中启动
异步协程时遇到的困惑,并提供一种在不阻塞主线程的情况下,类似 JavaScript 的方式立即执行
异步任务的方案。文章深入探讨了 `asyncio` 库的特性,并结合 `run_coroutine_threadsafe` 方法展示了如何在独立的事件循环中运行协程,从而实现异步任务的并行执行和状态监控。</p>
在 Python 的异步编程中,一个常见的困惑是如何立即启动一个异步协程,而无需立即 `aw
ait` 它。与 JavaScript 等语言不同,Python 的 `async` 函数在调用时并不会立即执行,而是返回一个协程对象。只有通过 `await`、`asyncio.gather` 等方式,协程才会真正开始执行。
这种行为可能导致一些问题,尤其是在需要尽快启动多个协程,并在稍后等待它们完成的情况下。如果使用 `asyncio.gather`,所有协程会在同一时刻启动,导致潜在的
性能瓶颈。
为了解决这个问题,我们可以利用 `asyncio.run_coroutine_threadsafe` 函数,在一个独立的线程中运行协程。这允许协程立即启动,而不会阻塞主线程。
**使用 `run_coroutine_threadsafe`**
`asyncio.run_coroutine_threadsafe(coro, loop)` 函数接受一个协程对象 `coro` 和一个事件循环 `loop` 作为参数。它将协程提交到指定的事件循环中,并在该循环中运行协程。该函数返回一个 `concurrent.futures.Future` 对象,可以用来获取协程的结果或检查其状态。
以下是一个示例代码,展示了如何使用 `run_coroutine_threadsafe` 函数:
```
python
import asyncio
import time
from threading import Thread
global_loop = None
def thread_for_event_loop():
global global_loop
global_loop = asyncio.new_event_loop()
asyncio.set_event_loop(global_loop)
global_loop.run_forever()
t = Thread(target=thread_for_event_loop)
t.daemon = True
t.start()
time.sleep(1) # wait for thread to start
old_print = print
print = lambda *_: old_print(round(time.perf_counter(), 1), *_)
def attempt(future): # doesn't actually do anything, only prints if task is done
print(future.done())
async def work():
print("SETUP")
await asyncio.sleep(2)
print("MIDDLE")
await asyncio.sleep(2)
print("END")
return "Result"
async def main():
print("START", int(time.perf_counter()))
task = asyncio.run_coroutine_threadsafe(work(), global_loop)
attempt(task)
attempt(task)
print("before first sleep")
time.sleep(3)
print("after first sleep")
attempt(task)
attempt(task)
print("before second sleep")
time.sleep(3) # Block CPU to wait for second sleeping to finish
print("after second sleep")
attempt(task)
attempt(task)
print(await asyncio.wrap_future(task))
asyncio.run(main())
代码解释:
-
创建事件循环线程: thread_for_event_loop 函数创建一个新的事件循环,并在一个独立的线程中运行它。这确保了协程在后台运行,而不会阻塞主线程。
-
attempt 函数: attempt 函数用于检查协程的状态。它接受一个 Future 对象作为参数,并打印协程是否完成。
-
work 协程: work 协程模拟一个耗时任务,其中包含多个 await asyncio.sleep() 调用。
-
main 协程: main 协程使用 asyncio.run_coroutine_threadsafe 函数将 work 协程提交到事件循环线程中。然后,它使用 attempt 函数检查协程的状态,并使用 time.sleep() 模拟主线程中的其他工作。
-
asyncio.wrap_future: 将 concurrent.futures.Future 包装成 asyncio 的 Future,以便使用 await 等待结果。
运行结果:
1.1 START 1
1.1 False
1.1 False
1.1 before first sleep
1.1 SETUP
3.1 MIDDLE
4.1 after first sleep
4.1 False
4.1 False
4.1 before second sleep
5.1 END
7.1 after second sleep
7.1 True
7.1 True
7.1 Result
登录后复制
注意事项:
- asyncio.run_coroutine_threadsafe 函数需要一个事件循环作为参数。如果当前线程没有事件循环,则需要创建一个新的事件循环。
- asyncio.run_coroutine_threadsafe 函数返回一个 concurrent.futures.Future 对象。可以使用 await asyncio.wrap_future(future) 等待 Future 对象完成并获取结果。
- 在多线程环境中使用 asyncio 时,需要注意线程安全问题。确保所有对 asyncio 对象的访问都是线程安全的。
总结:
通过使用 asyncio.run_coroutine_threadsafe 函数,我们可以实现类似 JavaScript 的异步编程模型,即立即启动异步任务,并在稍后等待它们完成。这种方法可以提高程序的性能和响应速度,尤其是在需要同时运行多个异步任务的情况下。
立即学习“Python免费学习笔记(深入)”;
以上就是在 Python 中无需等待即可启动或恢复异步方法/协程的详细内容,更多请关注php中文网其它相关文章!