是否可以 运行 只有异步事件循环的一个步骤
Is it possible to run only a single step of the asyncio event loop
我正在使用 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 事件循环的单次迭代。
像loop.run_once()
这样的public方法的缺失是故意的。
并非每个受支持的事件循环都有迭代一个步骤的方法。通常底层 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)
我使用以下程序创建自己的 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)
看看这个例子。
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())
我正在使用 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 事件循环的单次迭代。
像loop.run_once()
这样的public方法的缺失是故意的。
并非每个受支持的事件循环都有迭代一个步骤的方法。通常底层 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)
我使用以下程序创建自己的 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)
看看这个例子。
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())