簡體   English   中英

Python asyncio:如何在Task.cancel之間安全地在線程之間關閉事件循環?

[英]Python asyncio : how to close a event loop after Task.cancel safely among threads?

在我自己的項目中,我在線程中啟動事件循環,然后取消任務並安全地在另一個線程中關閉循環。 但是我失敗了。

讀取task-object后 ,我仍然不明白如何等待Task.cancel之后真正取消的任務

的Python版本:3.7.1

操作系統:windows

下面是我的調試過程。

import threading
import asyncio
import time

async def visit_sth():
    print("start sleep")
    await asyncio.sleep(3)
    print("end sleep")


class Person(object):

    def __init__(self):
        self.loop = asyncio.new_event_loop()

    def visit(self):

        asyncio.set_event_loop(self.loop)
        try:
            self.loop.run_until_complete(visit_sth())
        except Exception as err:
            print(err)

    def pause(self):

        tasks = asyncio.all_tasks(loop=self.loop)
        for t in tasks:
            t.cancel()

        self.loop.stop()
        self.loop.close()

P = Person()
t1 = threading.Thread(target=P.visit)
t2 = threading.Thread(target=P.pause)

t1.start()
time.sleep(0.5)
t2.start()

t1.join()
t2.join()

以下錯誤

start sleep
Exception in thread Thread-2:
Traceback (most recent call last):
  File "C:\Python3701\lib\threading.py", line 917, in _bootstrap_inner
    self.run()
  File "C:\Python3701\lib\threading.py", line 865, in run
    self._target(*self._args, **self._kwargs)
  File "c:\Users\zhouxin\Documents\jupyterlab\learning_asyncio\starkoverflow.py", line 31, in pause
    self.loop.close()
  File "C:\Python3701\lib\asyncio\selector_events.py", line 94, in close
    raise RuntimeError("Cannot close a running event loop")
RuntimeError: Cannot close a running event loop

取消后,事件循環仍在運行。

另外,文檔任務對象Task.cancel()不能保證任務將被取消

所以我轉向stackoverflow,並閱讀Kill任務而不是等待它們 ,並更改暫停方法,例如

def pause(self):

    tasks = asyncio.all_tasks(loop=self.loop)
    for t in tasks:
        t.cancel()
        with suppress(asyncio.CancelledError):
            self.loop.run_until_complete(t)

    self.loop.stop()
    self.loop.close()

發生另一個錯誤

start sleep
Exception in thread Thread-2:
Traceback (most recent call last):
  File "C:\Python3701\lib\threading.py", line 917, in _bootstrap_inner
    self.run()
  File "C:\Python3701\lib\threading.py", line 865, in run
    self._target(*self._args, **self._kwargs)
  File "c:\Users\zhouxin\Documents\jupyterlab\learning_asyncio\starkoverflow.py", line 31, in pause
    self.loop.run_until_complete(t)
  File "C:\Python3701\lib\asyncio\base_events.py", line 560, in run_until_complete
    self.run_forever()
  File "C:\Python3701\lib\asyncio\base_events.py", line 515, in run_forever
    raise RuntimeError('This event loop is already running')
RuntimeError: This event loop is already running

這種方式行不通。

現在,我真的很困惑如何等待任務取消,然后在Task.cancel之后關閉循環。

您的代碼有幾個問題:

  • pause從運行事件循環的線程外部與事件循環交互。 這是禁止的,必須替換為對run_coroutine_threadsafecall_soon_threadsafe調用。

  • 該代碼為每個業務對象創建一個新的事件循環。 這是不希望的,因為asyncio的強大功能是它允許在單個事件循環中包含許多協程。

推薦的模式是具有單個事件循環,並使用run_coroutine_threadsafe提交任務。 要取消任務時,不必停止整個循環,而只需取消該特定任務即可。 例如:

import threading
import asyncio
import time

async def visit_sth():
    print("start sleep")
    await asyncio.sleep(3)
    print("end sleep")


class Person:
    def visit(self):
        # returns a concurrent.futures.Future
        self._visit_fut = asyncio.run_coroutine_threadsafe(visit_sth(), loop)
        # result() waits for the future to be resolved and returns the
        # result
        try:
            return self._visit_fut.result()
        except Exception as err:
            print(type(err), err)

    def pause(self):
        self._visit_fut.cancel()

loop = asyncio.new_event_loop()
threading.Thread(target=loop.run_forever).start()

P = Person()
t1 = threading.Thread(target=P.visit)
t2 = threading.Thread(target=P.pause)

t1.start()
time.sleep(0.5)
t2.start()

t1.join()
t2.join()
loop.call_soon_threadsafe(loop.stop)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM