python3 协程 写同一个文件_python协程 -- 动态添加协程任务asyncio.run_coroutine_threadsafe...

本文介绍了如何在Python3中使用`asyncio.run_coroutine_threadsafe`在不同线程中动态添加协程任务到事件循环。通过创建一个在子线程中运行的事件循环,主线程可以生成协程任务并提交到子线程的事件循环中,以实现协程任务的并发执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用asyncio.run_coroutine_threadsafe动态添加协程任务到事件循环中

一. 官方文档

根据asyncio的文档介绍,asyncio的事件循环不是线程安全的,一个event loop只能在一个线程内调度和执行任务,

并且同一时间只有一个任务在运行,当程序调用get_event_loop获取event loop时,会从一个本地的Thread Local对象获取属于当前线程的event loop

run_coroutine_threadsafe内部用到了call_soon_threadsafe:

event loop内部会维护着一个self-pipe,它由一对socketpair组成,_write_to_self的作用就是把一个信号写到self-pipe的一端.

这样一来,event loop在检测到self-pipe发生事件后,就会响应并唤醒事件循环来处理任务

asyncio.run_coroutine_threadsafe(coro, loop)

1. 此方法提交一个协程任务到循环中,loop作为参数

2. 返回Futures供查询结果

3. 当事件循环运行时, 必须在不同线程下添加协程任务到此循环中

二. 简单例子

由于运行事件循环会阻塞当前线程, 为满足动态添加协程任务的需求

故将事件循环放在次线程中运行, 在主线程中创建协程任务, 然后提交到次线程里的循环中

#!/usr/bin/env python

# -*- coding: utf-8 -*-

# @File : test_coroutine.py

# @Author: Wade Cheung

# @Date : 2018/12/9

# @Desc : 动态添加协程任务

import asyncio

from threading import Thread

async def create_task(event_loop):

i = 0

while True:

# 每秒产生一个任务, 提交到线程里的循环中, event_loop作为参数

asyncio.run_coroutine_threadsafe(production(i), event_loop)

await asyncio.sleep(1)

i += 1

async def production(i):

while True:

print("第{}个coroutine任务".format(i))

await asyncio.sleep(1)

def start_loop(loop):

# 运行事件循环, loop作为参数

asyncio.set_event_loop(loop)

loop.run_forever()

thread_loop = asyncio.new_event_loop() # 创建事件循环

run_loop_thread = Thread(target=start_loop, args=(thread_loop,)) # 新起线程运行事件循环, 防止阻塞主线程

run_loop_thread.start() # 运行线程,即运行协程事件循环

main_loop = asyncio.new_event_loop()

main_loop.run_until_complete(create_task(thread_loop)) # 主线程负责create coroutine object

参考:

<think>好的,我需要回答用户关于如何使用asyncio.run_coroutine_threadsafe方法安全地在不同线程中运行协程的问题。首先,我得回忆一下asyncio的相关知识,特别是线程与协程交互的部分。 用户提到了run_coroutine_threadsafe,这个方法的作用是在一个正在运行的事件循环线程中安全地提交协程。我记得这个方法会返回一个concurrent.futures.Future对象,可以用来等待结果或处理异常。 接下来,我需要确定如何正确使用这个方法。可能需要分步骤说明:首先启动一个事件循环在单独线程中,然后从其他线程提交协程。需要示例代码来演示这个过程。 另外,要注意线程间的同步。比如,主线程需要获取子线程中的事件循环引用,可能需要使用threading.Event来确保事件循环已经启动。同时,处理异常和结果的部分也要提到,比如使用future.result()来获取结果或捕获异常。 还要注意asyncio的一些基本概念,比如事件循环只能在运行它的线程中被访问,否则会引发错误。run_coroutine_threadsafe正是为了解决跨线程调用而设计的,所以需要强调这一点。 可能还需要比较其他方法,比如loop.call_soon_threadsafe,但用户特别问的是run_coroutine_threadsafe,所以重点放在这里。 最后,要确保代码示例正确,包括导入必要的模块,启动子线程运行事件循环,以及正确提交协程并处理结果。同时,引用相关的参考资料,比如引用2提到的asyncio包,可能需要在回答中引用。 检查是否有遗漏的步骤,比如关闭事件循环和线程的正确退出方式,或者在协程中处理异常的情况。确保示例代码简洁明了,用户能够复制并理解。 总结,回答的结构应该是先解释方法的作用,然后逐步展示实现步骤,提供代码示例,最后讨论注意事项和常见问题。引用相关的资料来支持说明,比如引用2和可能引用5关于类型导入的问题,但这里可能不太相关,需看具体情况。</think>### 如何使用 asyncio.run_coroutine_threadsafe 实现线程安全的协程运行 在Python中,`asyncio.run_coroutine_threadsafe` 是跨线程安全执行协程的核心方法。它允许在一个已运行事件循环的线程中提交协程,并返回一个`concurrent.futures.Future`对象以处理结果或异常[^2]。 #### 实现步骤 1. **创建独立线程运行事件循环** 事件循环必须在目标线程中启动,例如通过子线程运行`loop.run_forever()`。 2. **提交协程到目标事件循环** 使用`asyncio.run_coroutine_threadsafe(coro, loop)`向目标事件循环提交协程,其中`loop`是目标线程的事件循环对象。 3. **处理返回的Future对象** 通过`future.result(timeout)`获取协程执行结果或捕获异常。 #### 示例代码 ```python import asyncio import threading # 子线程中运行事件循环 def start_event_loop(loop, ready_event): asyncio.set_event_loop(loop) ready_event.set() # 通知主线程事件循环已就绪 loop.run_forever() # 主线程逻辑 async def async_task(): await asyncio.sleep(1) return "协程执行完成" if __name__ == "__main__": # 创建子线程的事件循环 sub_loop = asyncio.new_event_loop() ready_event = threading.Event() thread = threading.Thread(target=start_event_loop, args=(sub_loop, ready_event)) thread.start() # 等待事件循环启动 ready_event.wait() try: # 提交协程到子线程的事件循环 future = asyncio.run_coroutine_threadsafe(async_task(), sub_loop) print(future.result(2)) # 阻塞等待结果 finally: # 清理资源 sub_loop.call_soon_threadsafe(sub_loop.stop) thread.join() ``` #### 关键注意事项 1. **线程绑定规则** 事件循环只能在其所属线程中被访问,跨线程直接调用`loop`方法会引发`RuntimeError`。 2. **同步机制** 使用`threading.Event`确保主线程在事件循环启动后再提交任务3. **异常处理** `future.result()`会抛出以下异常: - `TimeoutError`: 超时未完成 - 协程内部未捕获的异常 4. **资源释放** 通过`loop.call_soon_threadsafe(loop.stop)`安全停止事件循环,避免资源泄漏。 #### 典型应用场景 1. GUI应用中防止界面冻结 2. 多线程爬虫任务调度 3. 后台服务处理异步IO操作 例如,在PyQt5中可以通过该方法将耗时操作提交到子线程的协程中,保持界面响应性: ```python # 在按钮点击事件中提交任务 def on_button_click(self): future = asyncio.run_coroutine_threadsafe( fetch_data_from_api(), background_loop ) future.add_done_callback(self.update_ui) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值