目标:运行main()
,其中包含start_time
和end_time
之间的一堆异步函数
import datetime as dt
start_time, end_time= dt.time(9, 29), dt.time(16, 20)
current_time()
仅将当前时间添加到工作空间中。这段时间用于脚本中未在此处显示的几个不同点
async def current_time():
while True:
globals()['now'] = dt.datetime.now().replace(microsecond=0)
await asyncio.sleep(.001)
另一个执行某项功能的功能:
async def balance_check():
while True:
#do something here
await asyncio.sleep(.001)
main()
等待着先前的协程:
async def main():
while True:
#Issue:This is my issue. I am trying to make these
#coroutines only run between start_time and end_time.
#Outside this interval, I want the loop to
#shut down and for the script to stop.
if start_time < now.time() < end_time :
await asyncio.wait([
current_time(),
balance_check(),
])
else:
print('loop stopped since {} is outside {} and {}'.format(now, start_time, end_time))
loop.stop()
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
loop.run_until_complete(main())
finally:
loop.close()
问题 :即使在结束时间之后,它仍然可以正常工作
答案 0 :(得分:2)
问题是await asyncio.wait([current_time(), balance_check(),])
的错误使用。
等待asyncio.wait()
等待指定的等待对象完成 ,即返回值或引发异常。由于current_time
和balance_check
都从未退出无限循环,因此main()
的执行从未传递过await asyncio.wait(...)
,因为此表达式等待两者都结束。反过来,while
中的main()
循环永远不会进行第二次迭代,并且loop.stop()
没有机会运行。
如果代码的目的是使用asyncio.wait()
使协程有运行的机会,则asyncio.wait
并不是实现此目的的工具。取而代之的是,只需调用asyncio.create_task()
即可开始两项任务,然后不执行任何操作。只要事件循环可以运行(即不会被调用time.sleep()
或类似事件阻塞),asyncio就会自动在协程(在这种情况下为current_time
和balanced_check
)之间切换约1毫秒的速度。当然,您将希望在end_time
截止日期之前重新获得控制权,因此“不做任何事”最好表示为一次对asyncio.sleep()
的调用:
async def main():
t1 = asyncio.create_task(current_time())
t2 = asyncio.create_task(balance_check())
end = dt.datetime.combine(dt.date.today(), end_time)
now = dt.datetime.now()
await asyncio.sleep((end - now).total_seconds())
print('loop stopped since {} is outside {} and {}'.format(now, start_time, end_time))
t1.cancel()
t2.cancel()
请注意,甚至不需要显式loop.stop()
,因为一旦给定的协程完成,run_until_complete()
将自动停止循环。在任务上调用cancel()
没有实际效果,因为循环几乎立即停止了。它可以使这些任务完成,以便垃圾收集器不会警告您销毁待处理的任务。
如注释中所述,该代码不会等待start_time
,但是可以通过在生成任务之前添加另一个睡眠来轻松实现该功能。
答案 1 :(得分:1)
您可以使用synchronizing primitives',特别是events
。
import datetime as dt
import asyncio
async def function(start_event, end_event):
# Waits until we get the start event command.
await start_event.wait()
# Run our function until we get the end_event being set to true.
index = 0
while not end_event.is_set():
print(f"Function running; index {index}.")
await asyncio.sleep(2)
index += 1
async def controller(start_event, end_event):
# Will usually be this. Commented out for testing purposes. Can be passed by main.
# Subtraction/addition does not work on dt.datetime(). Inequality does (> or <)
# now, start, end = dt.datetime.now().time(), dt.time(17, 24), dt.time(18, 0, 30)
now = dt.datetime.now()
start, end = now + dt.timedelta(seconds=-10), now + dt.timedelta(seconds=10)
# Starts our function.
print(f"Starting at {start}. Ending in {end - now}. ")
start_event.set()
# Keeps checking until we pass the scheduled time.
while start < now < end:
print(f"Ending in {end - now}.")
await asyncio.sleep(2)
now = dt.datetime.now()
# Ends our function.
end_event.set()
print(f"Function ended at {now}.")
async def main():
# Creates two indepedent events.
# start_event is flagged when controller is ready for function to be ran (prevents misfires).
# end_event is flagged when controller see's start < time.now < end (line 30).
start_event = asyncio.Event()
end_event = asyncio.Event()
# Starts both functions at the same time.
await asyncio.gather(
controller(start_event, end_event), function(start_event, end_event)
)
if __name__ == "__main__":
# Starting our process.
asyncio.run(main())
此方法将同时需要function
和controller
来占用asyncio
循环中的空间。但是,function
将在大多数时间被锁定,而将是控制器使用其while
循环来占用循环资源,因此请牢记在心。