[英]Python - Cancel task in asyncio?
I have written code for async pool below.我在下面为异步池编写了代码。 in __aexit__
i'm cancelling the _worker tasks after the tasks get finished.在__aexit__
中,我将在任务完成后取消 _worker 任务。 But when i run the code, the worker tasks are not getting cancelled and the code is running forever.但是当我运行代码时,工作任务不会被取消并且代码会永远运行。 This what the task looks like: <Task pending coro=<AsyncPool._worker() running at \async_pool.py:17> wait_for=<Future cancelled>>
.这是任务的样子: <Task pending coro=<AsyncPool._worker() running at \async_pool.py:17> wait_for=<Future cancelled>>
。 The asyncio.wait_for
is getting cancelled but not the worker tasks. asyncio.wait_for
被取消,但没有工作任务。
class AsyncPool:
def __init__(self,coroutine,no_of_workers,timeout):
self._loop = asyncio.get_event_loop()
self._queue = asyncio.Queue()
self._no_of_workers = no_of_workers
self._coroutine = coroutine
self._timeout = timeout
self._workers = None
async def _worker(self):
while True:
try:
ret = False
queue_item = await self._queue.get()
ret = True
result = await asyncio.wait_for(self._coroutine(queue_item), timeout = self._timeout,loop= self._loop)
except Exception as e:
print(e)
finally:
if ret:
self._queue.task_done()
async def push_to_queue(self,item):
self._queue.put_nowait(item)
async def __aenter__(self):
assert self._workers == None
self._workers = [asyncio.create_task(self._worker()) for _ in range(self._no_of_workers)]
return self
async def __aexit__(self,type,value,traceback):
await self._queue.join()
for worker in self._workers:
worker.cancel()
await asyncio.gather(*self._workers, loop=self._loop, return_exceptions =True)
To use the Asyncpool:要使用异步池:
async def something(item):
print("got", item)
await asyncio.sleep(item)
async def main():
async with AsyncPool(something, 5, 2) as pool:
for i in range(10):
await pool.push_to_queue(i)
asyncio.run(main())
When you have an asyncio
task created and then cancelled, you still have the task alive that need to be "reclaimed".当您创建并取消了asyncio
任务时,您仍然有需要“回收”的任务。 So you want to await worker
for it.所以你想await worker
。 However, once you await
such a cancelled task, as it will never give you back the expected return value, the asyncio.CancelledError
will be raised and you need to catch it somewhere.但是,一旦您await
这样一个取消的任务,因为它永远不会给您返回预期的返回值, asyncio.CancelledError
将被引发,您需要在某个地方捕获它。
Because of this behavior, I don't think you should gather
them but to await
for each of the cancelled tasks, as they are supposed to return right away:由于这种行为,我认为您不应该gather
它们,而是await
每个取消的任务,因为它们应该立即返回:
async def __aexit__(self,type,value,traceback):
await self._queue.join()
for worker in self._workers:
worker.cancel()
for worker in self._workers:
try:
await worker
except asyncio.CancelledError:
print("worker cancelled:", worker)
The problem is that your except Exception
exception clause also catches cancellation, and ignores it.问题是您的except Exception
异常子句也捕获取消,并忽略它。 To add to the confusion, print(e)
just prints an empty line in case of a CancelledError
, which is where the empty lines in the output come from.为了增加混乱, print(e)
只是在CancelledError
的情况下打印一个空行,这是输出中空行的来源。 (Changing it to print(type(e))
shows what's going on.) (将其更改为print(type(e))
显示发生了什么。)
To correct the issue, change except Exception
to something more specific, like except asyncio.TimeoutError
.要更正此问题,请将except Exception
更改为更具体的内容,例如except asyncio.TimeoutError
。 This change is not needed in Python 3.8 where asyncio.CancelledError
no longer derives from Exception
, but from BaseException
, so except Exception
doesn't catch it.在 Python 3.8 中不需要此更改,其中asyncio.CancelledError
不再派生自Exception
,而是派生自BaseException
,因此except Exception
不会捕获它。
This appears to work.这似乎有效。 The event
is a counting timer and when it expires it cancels
the tasks.该event
是一个计数计时器,当它到期时,它会cancels
任务。
import asyncio
from datetime import datetime as dt
from datetime import timedelta as td
import random
import time
class Program:
def __init__(self):
self.duration_in_seconds = 20
self.program_start = dt.now()
self.event_has_expired = False
self.canceled_success = False
async def on_start(self):
print("On Start Event Start! Applying Overrides!!!")
await asyncio.sleep(random.randint(3, 9))
async def on_end(self):
print("On End Releasing All Overrides!")
await asyncio.sleep(random.randint(3, 9))
async def get_sensor_readings(self):
print("getting sensor readings!!!")
await asyncio.sleep(random.randint(3, 9))
async def evauluate_data(self):
print("checking data!!!")
await asyncio.sleep(random.randint(3, 9))
async def check_time(self):
if (dt.now() - self.program_start > td(seconds = self.duration_in_seconds)):
self.event_has_expired = True
print("Event is DONE!!!")
else:
print("Event is not done! ",dt.now() - self.program_start)
async def main(self):
# script starts, do only once self.on_start()
await self.on_start()
print("On Start Done!")
while not self.canceled_success:
readings = asyncio.ensure_future(self.get_sensor_readings())
analysis = asyncio.ensure_future(self.evauluate_data())
checker = asyncio.ensure_future(self.check_time())
if not self.event_has_expired:
await readings
await analysis
await checker
else:
# close other tasks before final shutdown
readings.cancel()
analysis.cancel()
checker.cancel()
self.canceled_success = True
print("cancelled hit!")
# script ends, do only once self.on_end() when even is done
await self.on_end()
print('Done Deal!')
async def main():
program = Program()
await program.main()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.