是否可以只运行asyncio事件循环的一个步骤

时间:2015-04-21 20:22:25

标签: python events tkinter tk python-asyncio

我正在使用asyncio和tkinter处理简单的图形网络应用程序。我遇到了将asyncio事件循环与Tk的主循环结合起来的问题。如果可能的话,我想在没有线程的情况下这样做,因为这些库(但特别是tkinter)都不是非常线程安全的。目前,我在asyncio协程中使用Tk.update,它只运行tk事件循环的一次迭代:

@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

但是,为了探索所有选项,我想知道是否可以执行相反的操作 - 如果可以在tk回调中仅调用asyncio事件循环的单次迭代。

3 个答案:

答案 0 :(得分:10)

loop.run_once()这样的公共方法缺失是故意的。 并非每个受支持的事件循环都有一个迭代一步的方法。通常,底层API具有创建事件循环并永久运行它的方法,但模拟单个步骤可能非常无效。

如果您真的需要它,您可以轻松实现单步迭代:

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)

答案 1 :(得分:1)

看看这个例子。

import asyncio
from tkinter import *

class asyncTk(Tk):
    def __init__(self):
        super().__init__()
        self.running = True
        self.protocol("WM_DELETE_WINDOW", self.on_closing)

    def on_closing(self):
        self.running = False
        self.destroy()
        
    def __await__(self):
        while self.running:
            self.update()
            yield

async def asd():
    for x in range(1,10):
        await asyncio.sleep(1)
        print(x)

async def main():
    w = asyncTk()
    asyncio.create_task(asd())
    await w

asyncio.run(main())

答案 2 :(得分:0)

我使用以下过程创建自己的run_once()run_forever()

这是一个简化的示例:

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)