Is it possible to run only a single step of the asyncio event loop

风流意气都作罢 提交于 2019-11-27 18:25:48

问题


I'm working on a simple graphical network application, using asyncio and tkinter. I'm running into the problem of combining the asyncio event loop with Tk's mainloop. If possible, I'd like to do it without threads, because both these libraries (but especially tkinter) aren't very thread safe. Currently, I'm using Tk.update in an asyncio coroutine, which runs only a single iteration of the tk event loop:

@asyncio.coroutine
def run_tk(tk, interval=0.1):
    try:
        while True:
            tk.update()
            yield from asyncio.sleep(interval)
    except TclError as e:
        if "application has been destroyed" not in e.args[0]:
            raise

However, in the interest of exploring all options, I was wondering if it was possible to do the reverse- if it was possible to invoke only a single iteration of an asyncio event loop inside a tk callback.


回答1:


The missing of public method like loop.run_once() is intentional. Not every supported event loop has a method to iterate one step. Often underlying API has methods for creating event loop and running it forever but emulating single step may be very ineffective.

If you really need it you may implement single-step iteration easy:

import asyncio


def run_once(loop):
    loop.call_soon(loop.stop)
    loop.run_forever()


loop = asyncio.get_event_loop()

for i in range(100):
    print('Iteration', i)
    run_once(loop)



回答2:


I using the following procedure for own run_once() and run_forever() creation.

Here's a simplified example:

import asyncio

async def worker(**kwargs):
    id = kwargs.get('id', '0.0.0.0.0.0')
    time = kwargs.get('time', 1)

    try:
        # Do stuff.
        print('start: ' + id)
    finally:
        await asyncio.sleep(time)

async def worker_forever(**kwargs):
    while True:
        await worker(**kwargs)

def init_loop(configs, forever=True):
    loop = asyncio.get_event_loop()

    if forever:
        tasks = [
            loop.create_task(worker_forever(id=conf['id'], time=conf['time'])) 
            for conf in config
        ]

    else:
        tasks = [
            asyncio.ensure_future(worker(id=conf['id'], time=conf['time'])) 
            for conf in configs
        ]

    return loop, tasks

def run_once(configs):
    print('RUN_ONCE')
    loop, futures = init_loop(configs, forever=False)
    result = loop.run_until_complete(asyncio.gather(*futures))
    print(result)

def run_forever(configs):
    print('RUN_FOREVER')
    loop, _ = init_loop(configs, forever=True)
    try:
        loop.run_forever()
    except KeyboardInterrupt:
        pass
    finally:
        print("Closing Loop")
        loop.close()

if __name__ == '__main__':
    configurations = [
        {'time': 5, 'id': '4'},
        {'time': 6, 'id': '5'},
        {'time': 1, 'id': '6'},
    ]  # TODO :: DUMMY

    run_once(configurations)
    run_forever(configurations)


来源:https://stackoverflow.com/questions/29782377/is-it-possible-to-run-only-a-single-step-of-the-asyncio-event-loop

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!