当 te coro 在睡眠超时之前完成时如何在 coro 中清理 asyncio.sleep()
How to cleanup asyncio.sleep() in a coro when te coro finishes before the sleep timeout
当此计数器被另一个计时器事件(stop_future)中断并完成时,我无法清理下面的 count_timer(睡眠)。
import asyncio
import datetime
from concurrent.futures import FIRST_COMPLETED
DISPLAY_DT_TIMEOUT = 7
MAX_DISPLAY_COUNT = 3
COUNT_TIMEOUT = 4
def stop(stop_future, display_count):
print('stop', display_count, datetime.datetime.now())
stop_future.set_result('Done!')
async def display_dt1():
display_count = 0
stop_future = asyncio.Future()
stop_handle = loop.call_later(DISPLAY_DT_TIMEOUT, stop, stop_future, display_count)
# loop until
while not stop_future.done() and display_count < MAX_DISPLAY_COUNT:
print('dt1-1', display_count, datetime.datetime.now())
count_timer = asyncio.sleep(COUNT_TIMEOUT) # cleanup ??
await asyncio.wait([count_timer, stop_future], return_when=FIRST_COMPLETED)
print('dt-2', display_count, datetime.datetime.now())
display_count += 1
if not stop_future.done():
# cleanup stop future
stop_handle.cancel()
stop_future.cancel()
async def wait_before_loop_close():
await asyncio.sleep(10)
loop = asyncio.get_event_loop()
coro = display_dt1()
loop.run_until_complete(coro)
# this print shows the count_timer is still pending
print('tasks-1', asyncio.Task.all_tasks())
# wait for the count_timer to finish
loop.run_until_complete(wait_before_loop_close())
loop.close()
print('tasks-2', asyncio.Task.all_tasks())
print('finished', datetime.datetime.now())
结果:
dt1-1 0 2015-11-10 15:20:58.200107
dt-2 0 2015-11-10 15:21:02.201654
dt1-1 1 2015-11-10 15:21:02.201654
stop 0 2015-11-10 15:21:05.186800 # stop interrupt
dt-2 1 2015-11-10 15:21:05.186800 # results in stop counting
tasks-1 {<Task pending coro=<sleep() running at D:\Python\Python35-32\lib\asyncio\tasks.py:495> wait_for=<Future pending cb=[Task._wakeup()]>>}
>>> tasks-2 set()
finished 2015-11-10 15:21:15.193669
Update-1:取消count_timer(包裹在未来)
async def display_dt1():
count_timer_task = None
display_count = 0
stop_future = asyncio.Future()
stop_handle = loop.call_later(DISPLAY_DT_TIMEOUT, stop, stop_future, display_count)
while not stop_future.done() and display_count < MAX_DISPLAY_COUNT:
print('dt1-1', display_count, datetime.datetime.now())
count_timer = asyncio.sleep(COUNT_TIMEOUT) # cleanup ??
count_timer_task = asyncio.ensure_future(count_timer)
await asyncio.wait([count_timer_task, stop_future], return_when=FIRST_COMPLETED)
print('dt-2', display_count, datetime.datetime.now())
display_count += 1
if count_timer_task and not count_timer_task.cancelled():
count_timer_task.cancel()
print('check-1', datetime.datetime.now(), count_timer_task)
if not stop_future.done():
stop_handle.cancel()
stop_future.cancel()
print('check-2', datetime.datetime.now(), stop_future)
结果:
dt1-1 0 2015-11-10 16:44:29.180372
dt-2 0 2015-11-10 16:44:33.180908
dt1-1 1 2015-11-10 16:44:33.180908
stop 0 2015-11-10 16:44:36.181062
dt-2 1 2015-11-10 16:44:36.181062
check-1 2015-11-10 16:44:36.181062 <Task pending coro=<sleep() running at D:\Python\Python35-32\lib\asyncio\tasks.py:495> wait_for=<Future cancelled>>
tasks-1 set()
>>> tasks-2 set()
finished 2015-11-10 16:44:46.181965
您已将 wait()
配置为 return,以在计时器结束或用户取消循环中的较早时间为准。听起来您想取消 count_timer
,因为它 不是 导致 wait
调用 return 的未来。您可以简单地通过询问两个期货中的哪一个已完成来了解这一点。
count_timer = asyncio.sleep(COUNT_TIMEOUT)
await asyncio.wait([count_timer, stop_future], return_when=FIRST_COMPLETED)
if not count_timer.done():
count_timer.cancel()
asyncio.wait_for
implements much of what you are trying to do. Combine it with asyncio.shield
以防止您的 Future 在超时到期时被取消:
await asyncio.wait_for(asyncio.shield(stop_future), COUNT_TIMEOUT)
编辑:如果不清楚,这将进入 while
循环,替换对 asyncio.wait
.
的调用
voscausa 更新:显示代码
async def display_dt1():
display_count = 0
stop_future = asyncio.Future()
stop_handle = loop.call_later(DISPLAY_DT_TIMEOUT, stop, stop_future, display_count)
while not stop_future.done() and display_count < MAX_DISPLAY_COUNT:
print('dt-1', display_count, datetime.datetime.now())
try:
await asyncio.wait_for(asyncio.shield(stop_future), COUNT_TIMEOUT)
except asyncio.TimeoutError:
print('timeout', datetime.datetime.now())
print('dt-2', display_count, datetime.datetime.now())
display_count += 1
if not stop_future.done():
stop_handle.cancel()
stop_future.cancel()
print('check-2', datetime.datetime.now(), stop_future)
有:
DISPLAY_DT_TIMEOUT = 10
MAX_DISPLAY_COUNT = 3
COUNT_TIMEOUT = 4
结果:
dt-1 0 2015-11-10 21:43:04.549782
timeout 2015-11-10 21:43:08.551319 # count timeout
dt-2 0 2015-11-10 21:43:08.551319
dt-1 1 2015-11-10 21:43:08.551319
timeout 2015-11-10 21:43:12.552880 # count timeout
dt-2 1 2015-11-10 21:43:12.552880
dt-1 2 2015-11-10 21:43:12.552880
stop 0 2015-11-10 21:43:14.554649 # stop timeout
dt-2 2 2015-11-10 21:43:14.555650
tasks-1 set()
tasks-2 set()
finished 2015-11-10 21:43:24.558510
当此计数器被另一个计时器事件(stop_future)中断并完成时,我无法清理下面的 count_timer(睡眠)。
import asyncio
import datetime
from concurrent.futures import FIRST_COMPLETED
DISPLAY_DT_TIMEOUT = 7
MAX_DISPLAY_COUNT = 3
COUNT_TIMEOUT = 4
def stop(stop_future, display_count):
print('stop', display_count, datetime.datetime.now())
stop_future.set_result('Done!')
async def display_dt1():
display_count = 0
stop_future = asyncio.Future()
stop_handle = loop.call_later(DISPLAY_DT_TIMEOUT, stop, stop_future, display_count)
# loop until
while not stop_future.done() and display_count < MAX_DISPLAY_COUNT:
print('dt1-1', display_count, datetime.datetime.now())
count_timer = asyncio.sleep(COUNT_TIMEOUT) # cleanup ??
await asyncio.wait([count_timer, stop_future], return_when=FIRST_COMPLETED)
print('dt-2', display_count, datetime.datetime.now())
display_count += 1
if not stop_future.done():
# cleanup stop future
stop_handle.cancel()
stop_future.cancel()
async def wait_before_loop_close():
await asyncio.sleep(10)
loop = asyncio.get_event_loop()
coro = display_dt1()
loop.run_until_complete(coro)
# this print shows the count_timer is still pending
print('tasks-1', asyncio.Task.all_tasks())
# wait for the count_timer to finish
loop.run_until_complete(wait_before_loop_close())
loop.close()
print('tasks-2', asyncio.Task.all_tasks())
print('finished', datetime.datetime.now())
结果:
dt1-1 0 2015-11-10 15:20:58.200107
dt-2 0 2015-11-10 15:21:02.201654
dt1-1 1 2015-11-10 15:21:02.201654
stop 0 2015-11-10 15:21:05.186800 # stop interrupt
dt-2 1 2015-11-10 15:21:05.186800 # results in stop counting
tasks-1 {<Task pending coro=<sleep() running at D:\Python\Python35-32\lib\asyncio\tasks.py:495> wait_for=<Future pending cb=[Task._wakeup()]>>}
>>> tasks-2 set()
finished 2015-11-10 15:21:15.193669
Update-1:取消count_timer(包裹在未来)
async def display_dt1():
count_timer_task = None
display_count = 0
stop_future = asyncio.Future()
stop_handle = loop.call_later(DISPLAY_DT_TIMEOUT, stop, stop_future, display_count)
while not stop_future.done() and display_count < MAX_DISPLAY_COUNT:
print('dt1-1', display_count, datetime.datetime.now())
count_timer = asyncio.sleep(COUNT_TIMEOUT) # cleanup ??
count_timer_task = asyncio.ensure_future(count_timer)
await asyncio.wait([count_timer_task, stop_future], return_when=FIRST_COMPLETED)
print('dt-2', display_count, datetime.datetime.now())
display_count += 1
if count_timer_task and not count_timer_task.cancelled():
count_timer_task.cancel()
print('check-1', datetime.datetime.now(), count_timer_task)
if not stop_future.done():
stop_handle.cancel()
stop_future.cancel()
print('check-2', datetime.datetime.now(), stop_future)
结果:
dt1-1 0 2015-11-10 16:44:29.180372
dt-2 0 2015-11-10 16:44:33.180908
dt1-1 1 2015-11-10 16:44:33.180908
stop 0 2015-11-10 16:44:36.181062
dt-2 1 2015-11-10 16:44:36.181062
check-1 2015-11-10 16:44:36.181062 <Task pending coro=<sleep() running at D:\Python\Python35-32\lib\asyncio\tasks.py:495> wait_for=<Future cancelled>>
tasks-1 set()
>>> tasks-2 set()
finished 2015-11-10 16:44:46.181965
您已将 wait()
配置为 return,以在计时器结束或用户取消循环中的较早时间为准。听起来您想取消 count_timer
,因为它 不是 导致 wait
调用 return 的未来。您可以简单地通过询问两个期货中的哪一个已完成来了解这一点。
count_timer = asyncio.sleep(COUNT_TIMEOUT)
await asyncio.wait([count_timer, stop_future], return_when=FIRST_COMPLETED)
if not count_timer.done():
count_timer.cancel()
asyncio.wait_for
implements much of what you are trying to do. Combine it with asyncio.shield
以防止您的 Future 在超时到期时被取消:
await asyncio.wait_for(asyncio.shield(stop_future), COUNT_TIMEOUT)
编辑:如果不清楚,这将进入 while
循环,替换对 asyncio.wait
.
voscausa 更新:显示代码
async def display_dt1():
display_count = 0
stop_future = asyncio.Future()
stop_handle = loop.call_later(DISPLAY_DT_TIMEOUT, stop, stop_future, display_count)
while not stop_future.done() and display_count < MAX_DISPLAY_COUNT:
print('dt-1', display_count, datetime.datetime.now())
try:
await asyncio.wait_for(asyncio.shield(stop_future), COUNT_TIMEOUT)
except asyncio.TimeoutError:
print('timeout', datetime.datetime.now())
print('dt-2', display_count, datetime.datetime.now())
display_count += 1
if not stop_future.done():
stop_handle.cancel()
stop_future.cancel()
print('check-2', datetime.datetime.now(), stop_future)
有:
DISPLAY_DT_TIMEOUT = 10
MAX_DISPLAY_COUNT = 3
COUNT_TIMEOUT = 4
结果:
dt-1 0 2015-11-10 21:43:04.549782
timeout 2015-11-10 21:43:08.551319 # count timeout
dt-2 0 2015-11-10 21:43:08.551319
dt-1 1 2015-11-10 21:43:08.551319
timeout 2015-11-10 21:43:12.552880 # count timeout
dt-2 1 2015-11-10 21:43:12.552880
dt-1 2 2015-11-10 21:43:12.552880
stop 0 2015-11-10 21:43:14.554649 # stop timeout
dt-2 2 2015-11-10 21:43:14.555650
tasks-1 set()
tasks-2 set()
finished 2015-11-10 21:43:24.558510