![](/img/trans.png)
[英]python asyncio - cancelling a `to_thread` task won't stop the thread
[英]Python3 asyncio event loops and task cancelling
我一直在嘗試理解asyncio
模塊以實現服務器。 我在看這個問題,它似乎問了一個類似的問題,如果不是同一個問題,但我仍在努力掌握幕后發生的工作流程。
我有以下簡單的程序,它有兩個協程,一個從終端讀取並將其放入隊列,另一個協程等待隊列中的項目並將它們簡單地打印回屏幕。
import asyncio
q = asyncio.Queue()
async def put():
while True:
await q.put(input()) #Input would be normaly something like client.recv()
await asyncio.sleep(1) #This is neccessarry but I dont understand why
async def get():
while True:
print(await q.get())
def run():
loop = asyncio.get_event_loop()
task1 = loop.create_task(put())
task2 = loop.create_task(get())
loop.run_forever()
run()
該程序按預期工作,但是當從put
方法中刪除await asyncio.sleep(1)
語句時,它停止工作。 我假設是因為循環不斷消耗線程並且消息沒有被推送。 我不明白為什么,因為我認為input()
將是一個阻塞 function,因此協程應該暫停,直到 tty 上有新行可用。
第二個問題是,如果我在run()
調用中使用asyncio.get_event_loop()
,解釋器會警告我沒有活動循環,但是,如文檔中所述,此調用已棄用,因此我嘗試用asyncio.new_event_loop()
替換它asyncio.new_event_loop()
。 該程序仍然工作相同,但是我在 KeyboardInterrupt 上得到了回溯(調用asyncio.get_event_loop()
時不會發生)
Task was destroyed but it is pending!
task: <Task pending name='Task-1' coro=<put() running at test.py:10> wait_for=<Future pending cb=[Task.task_wakeup()]>>
Task was destroyed but it is pending!
task: <Task pending name='Task-2' coro=<get() running at test.py:15> wait_for=<Future pending cb=[Task.task_wakeup()]>>
Exception ignored in: <coroutine object get at 0x7f32f51369d0>
Traceback (most recent call last):
File "test.py", line 15, in get
File "/usr/lib64/python3.10/asyncio/queues.py", line 161, in get
File "/usr/lib64/python3.10/asyncio/base_events.py", line 745, in call_soon
File "/usr/lib64/python3.10/asyncio/base_events.py", line 510, in _check_closed
RuntimeError: Event loop is closed
我嘗試的第三個變體是使 run 方法本身異步並通過asyncio.run(run())
調用調用它。
import asyncio
q = asyncio.Queue()
async def put():
while True:
await q.put(input())
await asyncio.sleep(1)
async def get():
while True:
print(await q.get())
async def run():
loop = asyncio.get_event_loop()
task1 = loop.create_task(put())
task2 = loop.create_task(get())
await task1
asyncio.run(run())
這也很好用,但是如果我用await task2
替換await task1
,當我中斷程序時我會再次遇到錯誤。 為什么這些之間的執行不同,最后應該如何完成?
如評論中所述,使用StreamReader
(您的原始代碼)問題 #1 將完美無缺地工作並且不會引起任何問題。 input()
不給 aio 切換協程的機會,如果StreamReader
不斷有數據,您可以嘗試將隊列限制在一定長度。
對於問題 #2,在清理期間,Python 為當前線程使用分配的循環。 在底層, asyncio.run()
和asyncio.get_event_loop()
將循環分配給主線程。 當它找不到循環時,一切都會崩潰。
如果你想自己分配它,你可以這樣做:
def run():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
task1 = loop.create_task(put())
task2 = loop.create_task(get())
loop.run_forever()
請記住,您仍然缺少一些手動清理(即shutdown_asyncgens
和shutdown_executor
),但這是針對不同主題的。 總的來說,使用asyncio.run()
通常是正確的選擇。
我無法重現問題 #3, await task1
和await task2
都可以完美地工作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.