为何datetime.strptime()会阻塞asyncio.Queue.get()操作?
为什么datetime.strptime()会阻塞asyncio.Queue.get()操作?
问题代码
import time import asyncio from datetime import datetime from functools import partial def write(queue): data = dict(fname = "Stack", lname = "Overflow") # 注释掉这行代码后程序运行正常 dob = datetime.strptime("2025-02-12", "%Y-%m-%d") queue.put_nowait(data) print("Writing to queue complete") time.sleep(3600) # 永久休眠 async def reader(queue): loop = asyncio.get_running_loop() print("This print means this thread is not blocked") while msg := await queue.get(): print("Got my message", msg) loop.call_soon(print, msg) async def main(): queue = asyncio.Queue() loop = asyncio.get_running_loop() loop.run_in_executor(None, partial(write, queue)) await reader(queue) asyncio.run(main())
两种运行结果
- 注释
datetime.strptime()后的输出:
Writing to queue complete This print means this thread is not blocked Got my message {'fname': 'Stack', 'lname': 'Overflow'} {'fname': 'Stack', 'lname': 'Overflow'}
- 保留
datetime.strptime()后的输出:
This print means this thread is not blocked Writing to queue complete
问题根源
根本不是datetime.strptime()在阻塞队列,是你在非事件循环线程里直接调用asyncio.Queue.put_nowait()的操作本身就不安全。
asyncio.Queue是给协程间通信设计的,它的方法只有在事件循环所在的主线程里调用才是安全的。如果要在其他线程(比如run_in_executor启动的线程)里操作队列,必须用loop.call_soon_threadsafe()来包装队列操作。
之前注释掉strptime时能正常运行,纯粹是线程调度的运气好——write线程执行put_nowait的速度太快,队列内部状态没被线程竞争破坏,事件循环线程刚好及时处理了队列的唤醒信号。
而加上strptime后,这个操作让write线程的执行时间稍微变长,线程调度时机发生变化,导致不安全的put_nowait操作破坏了队列的内部状态,使得await queue.get()没有被正确唤醒,后续的打印代码自然就没机会执行。
修复方案
修改write函数,用事件循环的线程安全方法调用队列的put_nowait:
def write(queue, loop): data = dict(fname = "Stack", lname = "Overflow") dob = datetime.strptime("2025-02-12", "%Y-%m-%d") # 用线程安全的方式执行队列put操作 loop.call_soon_threadsafe(queue.put_nowait, data) print("Writing to queue complete") time.sleep(3600) async def main(): queue = asyncio.Queue() loop = asyncio.get_running_loop() # 把事件循环对象传给write函数 loop.run_in_executor(None, partial(write, queue, loop)) await reader(queue)
修改后不管有没有datetime.strptime(),程序都会正常输出所有内容。
内容的提问来源于stack exchange,提问作者Dinesh
相关产品推荐
相关产品推荐

