在一个时间间隔内运行Asyncio循环

时间:2019-10-30 20:43:40

标签: python python-asyncio

目标:运行main(),其中包含start_timeend_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()

问题 :即使在结束时间之后,它仍然可以正常工作

2 个答案:

答案 0 :(得分:2)

问题是await asyncio.wait([current_time(), balance_check(),])的错误使用。

等待asyncio.wait()等待指定的等待对象完成 ,即返回值或引发异常。由于current_timebalance_check都从未退出无限循环,因此main()的执行从未传递过await asyncio.wait(...),因为此表达式等待两者都结束。反过来,while中的main()循环永远不会进行第二次迭代,并且loop.stop()没有机会运行。

如果代码的目的是使用asyncio.wait()使协程有运行的机会,则asyncio.wait并不是实现此目的的工具。取而代之的是,只需调用asyncio.create_task()即可开始两项任务,然后不执行任何操作。只要事件循环可以运行(即不会被调用time.sleep()或类似事件阻塞),asyncio就会自动在协程(在这种情况下为current_timebalanced_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())

此方法将同时需要functioncontroller来占用asyncio循环中的空间。但是,function将在大多数时间被锁定,而将是控制器使用其while循环来占用循环资源,因此请牢记在心。