Python 如何创建一个事件循环,滚动协程永远运行在它上面?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30460929/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 08:27:34  来源:igfitidea点击:

How to create an event loop with rolling coroutines running on it forever?

pythonpython-3.xasynchronouscoroutinepython-asyncio

提问by changyuheng

In order to prevent from context switching, I want to create a big loop to serve both the network connections and some routines.

为了防止上下文切换,我想创建一个大循环来为网络连接和一些例程提供服务。

Here's the implementation for normal functions:

下面是普通函数的实现:

import asyncio
import time


def hello_world(loop):
    print('Hello World')
    loop.call_later(1, hello_world, loop)

def good_evening(loop):
    print('Good Evening')
    loop.call_later(1, good_evening, loop)

print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()

print('step: loop.call_soon(hello_world, loop)')
loop.call_soon(hello_world, loop)
print('step: loop.call_soon(good_evening, loop)')
loop.call_soon(good_evening, loop)

try:
    # Blocking call interrupted by loop.stop()
    print('step: loop.run_forever()')
    loop.run_forever()
except KeyboardInterrupt:
    pass
finally:
    print('step: loop.close()')
    loop.close()

Here's the implementation for coroutines:

这是协程的实现:

import asyncio


@asyncio.coroutine
def hello_world():
    while True:
        yield from asyncio.sleep(1)
        print('Hello World')

@asyncio.coroutine
def good_evening():
    while True:
        yield from asyncio.sleep(1)
        print('Good Evening')

print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()
try:
    print('step: loop.run_until_complete()')
    loop.run_until_complete(asyncio.wait([
        hello_world(),
        good_evening()
    ]))
except KeyboardInterrupt:
    pass
finally:
    print('step: loop.close()')
    loop.close()

And the mixed one:

和混合的:

import asyncio
import time


def hello_world(loop):
    print('Hello World')
    loop.call_later(1, hello_world, loop)

def good_evening(loop):
    print('Good Evening')
    loop.call_later(1, good_evening, loop)

@asyncio.coroutine
def hello_world_coroutine():
    while True:
        yield from asyncio.sleep(1)
        print('Hello World Coroutine')

@asyncio.coroutine
def good_evening_coroutine():
    while True:
        yield from asyncio.sleep(1)
        print('Good Evening Coroutine')

print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()

print('step: loop.call_soon(hello_world, loop)')
loop.call_soon(hello_world, loop)
print('step: loop.call_soon(good_evening, loop)')
loop.call_soon(good_evening, loop)
print('step: asyncio.async(hello_world_coroutine)')
asyncio.async(hello_world_coroutine())
print('step: asyncio.async(good_evening_coroutine)')
asyncio.async(good_evening_coroutine())

try:
    loop.run_forever()
except KeyboardInterrupt:
    pass
finally:
    print('step: loop.close()')
    loop.close()

As you see, each coroutine function has a while loop surrounded. How can I make it like the normal one? I.e. when it is done, call itself after the given delay time, but not just put a loop there.

如您所见,每个协程函数都有一个 while 循环。我怎样才能让它像正常的一样?即完成后,在给定的延迟时间后调用自己,但不仅仅是在那里放置一个循环。

采纳答案by dano

If you really want to eliminate the while-loop from the coroutines (I'm not sure why you feel that's necessary; it's the most natural way to do what you're trying to do), you can use asyncio.async(or asyncio.ensure_futureon Python 3.4.4+) to schedule the coroutine to run again on the next event loop iteration:

如果你真的想从协程中消除 while 循环(我不确定你为什么觉得这是必要的;这是做你想做的事情的最自然的方式),你可以使用asyncio.async(或asyncio.ensure_future在 Python 3.4. 4+) 安排协程在下一个事件循环迭代中再次运行:

import asyncio

@asyncio.coroutine
def hello_world():
    yield from asyncio.sleep(1)
    print('Hello World')
    asyncio.async(hello_world())

@asyncio.coroutine
def good_evening():
    yield from asyncio.sleep(1)
    print('Good Evening')
    asyncio.async(good_evening())

print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()
try:
    print('step: loop.run_until_complete()')
    asyncio.async(hello_world())
    asyncio.async(good_evening())
    loop.run_forever()
except KeyboardInterrupt:
    pass
finally:
    print('step: loop.close()')
    loop.close()

Note that you have to switch back to using loop.run_forever()if you do this, since hello_world/good_eveningwill exit immediately after printing now.

请注意,loop.run_forever()如果您这样做,您必须切换回使用,因为hello_world/good_evening将在打印后立即退出。

回答by uranusjr

Did you actually try to run the three examples you gave? The difference in behaviour is pretty obvious.

您是否真的尝试运行您提供的三个示例?行为上的差异非常明显。

Since you never said what you expect, there's not telling what is right and what is not. All three implementations could be right or wrong. I can tell you what behaviour each implementation has, and why it has such behaviour; only you know whether it is correct.

因为你从来没有说出你所期望的,所以没有人告诉你什么是对的,什么不是。所有三种实现都可能是对的,也可能是错的。我可以告诉你每个实现有什么行为,以及为什么会有这样的行为;只有你知道它是否正确。



In the second example (yield from asyncio.sleep(1)), the two coroutines are run concurrently. This means that each will execute on their own; hello_worldprints Hello Worldevery second, and good_eveningprints Good Eveningevery second.

在第二个示例 ( yield from asyncio.sleep(1)) 中,两个协程同时运行。这意味着每个人都将自行执行;hello_world打印Hello World每一秒,而good_evening打印Good Evening每一秒。

The other two examples both use time.sleep(1), which is blocking. This means that when the first function (whichever that is; let's say it's hello_world) reaches time.sleep(1), the whole program will hang for one second. This means that when hello_worldsleeps, good_eveningcannot run either, and vice versa.

其他两个示例都使用time.sleep(1),这是阻塞的。这意味着当第一个函数(无论是哪个;假设它是hello_world)到达 时time.sleep(1)整个程序将挂起一秒钟。这意味着当hello_world睡眠时,good_evening也不能运行,反之亦然。

The program executes like this:

程序是这样执行的:

  1. The loop is entered.
  2. The loop calls hello_world.
  3. time.sleep(1)in hello_worldis reached. The program sleeps for one second.
  4. Hello Worldprinted.
  5. hello_worldyields.
  6. The loop calls good_evening.
  7. Good Eveningprinted.
  8. time.sleep(1)in good_eveningis reached. The program sleeps for one second.
  9. good_eveningyields.
  10. Go to 2.
  1. 进入循环。
  2. 循环调用hello_world.
  3. time.sleep(1)hello_world达到。程序休眠一秒钟。
  4. Hello World打印。
  5. hello_world产量。
  6. 循环调用good_evening.
  7. Good Evening打印。
  8. time.sleep(1)good_evening达到。程序休眠一秒钟。
  9. good_evening产量。
  10. 转到 2。

Therefore both Hello Worldand Good Eveningappear every twoseconds, because there are two time.sleep(1)calls between each print. You would easily notice that if you run the two examples.

因此,既Hello WorldGood Evening每出现2秒,因为有两个time.sleep(1)各自之间的呼叫print。如果您运行这两个示例,您会很容易注意到这一点。

回答by changyuheng

import asyncio


@asyncio.coroutine
def hello_world_coroutine():
    yield from asyncio.sleep(1)
    print('Hello World Coroutine')
    yield from hello_world_coroutine()

@asyncio.coroutine
def good_evening_coroutine():
    yield from asyncio.sleep(1)
    print('Good Evening Coroutine')
    yield from good_evening_coroutine()

print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()
try:
    print('step: loop.run_until_complete()')
    loop.run_until_complete(asyncio.wait([
        hello_world_coroutine(),
        good_evening_coroutine()
    ]))
except KeyboardInterrupt:
    pass
finally:
    print('step: loop.close()')
    loop.close()

UPD

UPD

This code would reach the maximum recursion depth. Might because Python has no tail call optimization. Leave the code here as a wrong example.

此代码将达到最大递归深度。可能是因为 Python 没有尾调用优化。将此处的代码留作错误示例。

回答by Down the Stream

# asyncio_coroutine_forever.py

import asyncio

async def hello_world():

    await asyncio.sleep(1)
    print('Hello World')
    await good_evening()


async def good_evening():

    await asyncio.sleep(1)
    print('Good Evening')
    await hello_world()


loop = asyncio.get_event_loop()

try:

    loop.run_until_complete(hello_world())
    loop.run_until_complete(good_evening())
    loop.run_forever()

finally:

    print('closing event loop')
    loop.close()