Asyncio两个循环用于不同的I / O任务?

时间:2015-07-25 05:12:20

标签: python python-3.x python-asyncio

我正在使用Python3 Asyncio模块来创建负载平衡应用程序。我有两个繁重的IO任务:

  • SNMP轮询模块,用于确定最佳服务器
  • A"类似代理"模块,用于平衡请求与所选服务器的连接。

这两个进程将永远运行,彼此独立,不应被另一个进程阻止。

我不能使用1个事件循环,因为它们会阻塞彼此,是否有任何方法可以有2个事件循环或者我是否必须使用多线程/处理?

我尝试使用asyncio.new_event_loop(),但还没设法让它工作。

7 个答案:

答案 0 :(得分:18)

回答我自己的问题以发布我的解决方案:

我最终做的是在线程内为轮询模块创建一个线程和一个新的事件循环,所以现在每个模块都在不同的循环中运行。它不是一个完美的解决方案,但它是唯一对我有意义的解决方案(我想避免线程,但因为它只有一个......)。例如:

import asyncio
import threading


def worker():
    second_loop = asyncio.new_event_loop()
    execute_polling_coroutines_forever(second_loop)
    return

threads = []
t = threading.Thread(target=worker)
threads.append(t)
t.start()

loop = asyncio.get_event_loop()
execute_proxy_coroutines_forever(loop)

Asyncio要求每个循环在同一个线程中运行其协同程序。使用这个方法你有一个事件循环foreach线程,它们是完全独立的:每个循环将在它自己的线程上执行它的协同程序,所以这不是问题。 正如我所说,它可能不是最好的解决方案,但它对我有用。

答案 1 :(得分:7)

asyncio的全部要点是,您可以同时运行数千个I / O繁重的任务,因此根本不需要Thread,这正是{{1 }}是为。只需在同一循环中运行两个协程(SNMP和代理)即可。 在调用asyncio之前,必须使它们都可用于事件循环。像这样:

loop.run_forever()

我不知道您的代码的结构,因此不同的模块可能具有自己的无限循环或类似的内容,在这种情况下,您可以运行以下代码:

import asyncio

async def snmp():
    print("Doing the snmp thing")
    await asyncio.sleep(1)

async def proxy():
    print("Doing the proxy thing")
    await asyncio.sleep(2)

async def main():
    while True:
        await snmp()
        await proxy()

loop = asyncio.get_event_loop()
loop.create_task(main())
loop.run_forever()

请记住,import asyncio async def snmp(): while True: print("Doing the snmp thing") await asyncio.sleep(1) async def proxy(): while True: print("Doing the proxy thing") await asyncio.sleep(2) loop = asyncio.get_event_loop() loop.create_task(snmp()) loop.create_task(proxy()) loop.run_forever() snmp都必须是以异步感知方式编写的协程(proxy)。 async def不会使简单的阻止Python函数突然“异步”。

在您的特定情况下,我怀疑您有点困惑(没有冒犯!),因为编写良好的异步模块永远不会在同一循环中互相阻塞。在这种情况下,您根本不需要asyncio,只需在单独的asyncio中运行其中一个即可,而无需处理任何Thread东西。

答案 2 :(得分:1)

Asyncio事件循环是一个运行的单个线程,它不会并行运行任何东西,它是如何设计的。我能想到的最接近的事情是使用asyncio.wait

from asyncio import coroutine
import asyncio

@coroutine
def some_work(x, y):
    print("Going to do some heavy work")
    yield from asyncio.sleep(1.0)
    print(x + y)

@coroutine
def some_other_work(x, y):
    print("Going to do some other heavy work")
    yield from asyncio.sleep(3.0)
    print(x * y)



if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait([asyncio.async(some_work(3, 4)), 
                            asyncio.async(some_other_work(3, 4))]))
    loop.close()

另一种方法是使用asyncio.gather() - 它会从给定的期货清单中返回未来的结果。

tasks = [asyncio.Task(some_work(3, 4)), asyncio.Task(some_other_work(3, 4))]
loop.run_until_complete(asyncio.gather(*tasks))

答案 3 :(得分:1)

但我这样使用它,但仍然是同步没有异步:

ValueError: could not convert string to float: 'Iris-setosa'

如果收到的值包含(name =" calculator2")我等待5秒钟 如果没有,只需立即回答并写入数据。 但是在测试它时,首先将数据发送到服务器,其中包含(name =" calculator2")和下一个数据没有(name =" calculator2"),但是下一个数据在第一个5秒后处理完成后,第二个数据将被处理。

它的顺序。怎么了? 另一方面,我应该如何获得客户端连接的ip和端口?

答案 4 :(得分:1)

尽管在大多数情况下,使用Node时并不需要运行多个事件循环,但是人们不应该假设自己的假设适用于所有情况,也不要仅仅给您他们认为更好的假设而无需直接针对您的目标原始问题。

这里是演示如何在线程中创建新事件循环的演示。与您自己的answer相比,asyncio的诀窍是您每次执行基于异步的操作时都不要传递set_event_loop对象。

loop

理想情况下,您希望将繁重的工作放在工作线程中,并使asncyio线程尽可能轻地运行。可以将asyncio线程视为台式机或移动应用程序的GUI线程,而又不想阻止它。工作线程通常很忙,这是您不想在工作线程中创建单独的异步事件循环的原因之一。这是一个如何通过单个asyncio事件循环管理繁重工作线程的示例。这是这种用例中最常见的做法:

import asyncio
import threading


async def print_env_info_async():
    # As you can see each work thread has its own asyncio event loop.
    print(f"Thread: {threading.get_ident()}, event loop: {id(asyncio.get_running_loop())}")


async def work():
    while True:
        await print_env_info_async()
        await asyncio.sleep(1)


def worker():
    new_loop = asyncio.new_event_loop()
    asyncio.set_event_loop(new_loop)
    new_loop.run_until_complete(work())
    return


number_of_threads = 2
for _ in range(number_of_threads):
    threading.Thread(target=worker).start()

答案 5 :(得分:1)

我知道这是一个旧线程,但它可能仍然对某人有帮助。 我不擅长 asyncio,但这里是@kissgyorgy 答案的改进解决方案。我们不是单独等待每个闭包,而是创建任务列表并稍后触发它们(python 3.9):

import asyncio

async def snmp():
    while True:
        print("Doing the snmp thing")
        await asyncio.sleep(0.4)

async def proxy():
    while True:
        print("Doing the proxy thing")
        await asyncio.sleep(2)

async def main():
        tasks = []
        tasks.append(asyncio.create_task(snmp()))
        tasks.append(asyncio.create_task(proxy()))

        await asyncio.gather(*tasks)

asyncio.run(main())

结果:

Doing the snmp thing
Doing the proxy thing
Doing the snmp thing
Doing the snmp thing
Doing the snmp thing
Doing the snmp thing
Doing the proxy thing

答案 6 :(得分:0)

  

如果代理服务器一直在运行,则无法来回切换。代理侦听客户端请求并使它们异步,但另一个任务无法执行,因为这个任务永远在服务。

如果代理是一个协程并且正在使SNMP-poller挨饿(永远不会等待),那么客户端是否还要求饿死呢?

  

每个协程都将永远运行,它们不会结束

这应该没问题,只要它们await/yield fromecho server也将永远运行,并不意味着您无法在同一循环中运行多个服务器(虽然在不同的端口上)。