同步睡眠进入asyncio协同

时间:2017-02-16 16:33:59

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

我有一个协程如下:

async def download():
    downloader = DataManager()
    downloader.download()

DataManager.download()方法如下:

def download(self):
    start_multiple_docker_containers()
    while True:
        check_containers_statuses()
        sleep(N)  # synchronous sleep from time module

这是一个好习惯吗?如果不是,我如何在asyncio.sleep中使用download()

或许这样的代码结构在概念上是错误的?

3 个答案:

答案 0 :(得分:7)

这是我的解决方案:

import asyncio
import time


# Mocks of domain-specific functions
# ----------------------------------

def get_container_status(container_id, initial_time):
    """This mocks container status to change to 'exited' in 10 seconds"""
    if time.time() - initial_time < 10:
        print("%s: container %s still running" % (time.time(), container_id))
        return 'running'
    else:
        print("%s: container %s exited" % (time.time(), container_id))
        return 'exited'

def is_download_complete(container_id, initial_time):
    """This mocks download finished in 20 seconds after program's start"""
    if time.time() - initial_time < 20:
        print("%s: download from %s in progress" % (time.time(), container_id))
        return False
    else:
        print("%s: download from %s done" % (time.time(), container_id))
        return True

def get_downloaded_data(container_id):
    return "foo"


# Coroutines
# ----------

async def container_exited(container_id, initial_time):
    while True:
        await asyncio.sleep(1) # == setTimeout(1000), != sleep(1000)
        if get_container_status(container_id, initial_time) == 'exited':
            return container_id

async def download_data_by_container_id(container_id, initial_time):
    container_id = await container_exited(container_id, initial_time)
    while True:
        await asyncio.sleep(1)
        if is_download_complete(container_id, initial_time):
            return get_downloaded_data(container_id)


# Main loop
# ---------

if __name__ == "__main__":

    initial_time = time.time()

    loop = asyncio.get_event_loop()

    tasks = [
        asyncio.ensure_future(download_data_by_container_id("A", initial_time)),
        asyncio.ensure_future(download_data_by_container_id("B", initial_time))
    ]

    loop.run_until_complete(asyncio.wait(tasks))

    loop.close()

结果:

1487334722.321165: container A still running
1487334722.321412: container B still running
1487334723.325897: container A still running
1487334723.3259578: container B still running
1487334724.3285959: container A still running
1487334724.328662: container B still running
1487334725.3312798: container A still running
1487334725.331337: container B still running
1487334726.3340318: container A still running
1487334726.33409: container B still running
1487334727.336779: container A still running
1487334727.336842: container B still running
1487334728.339425: container A still running
1487334728.339506: container B still running
1487334729.34211: container A still running
1487334729.342168: container B still running
1487334730.3448708: container A still running
1487334730.34493: container B still running
1487334731.34754: container A exited
1487334731.347598: container B exited
1487334732.350253: download from A in progress
1487334732.3503108: download from B in progress
1487334733.354369: download from A in progress
1487334733.354424: download from B in progress
1487334734.354686: download from A in progress
1487334734.3548028: download from B in progress
1487334735.358371: download from A in progress
1487334735.358461: download from B in progress
1487334736.3610592: download from A in progress
1487334736.361115: download from B in progress
1487334737.363115: download from A in progress
1487334737.363211: download from B in progress
1487334738.3664992: download from A in progress
1487334738.36656: download from B in progress
1487334739.369131: download from A in progress
1487334739.36919: download from B in progress
1487334740.371079: download from A in progress
1487334740.37119: download from B in progress
1487334741.374521: download from A done
1487334741.3745651: download from B done

至于sleep()功能 - 不,你不应该使用它。它阻止整个python解释器1秒钟,这不是你想要的。

请记住,您没有并行(线程等),您有并发

即。你有一个只有一个执行线程的python解释器,你的主循环和所有协程运行,相互抢占。您希望您的解释器在该主循环中花费99.999%的工作时间,由asyncio创建,轮询套接字并等待超时。

所有协程应该尽可能快地返回并且绝对不应该包含阻塞sleep - 如果你调用它,它会阻塞整个解释器并阻止主循环从套接字获取信息或运行协程响应数据,到达那些插座。

所以,你应该等待asyncio.sleep()本质上等同于Javascript&#39; setTimeout() - 它只是告诉主循环在某个时间它应该唤醒这个协程并继续运行它

建议阅读:

答案 1 :(得分:3)

这很可能是一种不好的做法,因为time.sleep()会阻止所有内容,而你只想阻止特定的协程(我猜)。

您正在异步世界中进行同步操作。

以下模式怎么样?

async def download():
    downloader = DataManager()
    downloader.start_multiple_docker_containers()
    while True:
        downloader.check_containers_statuses()
        await syncio.sleep(N)

答案 2 :(得分:0)

我是asyncio的新手,但似乎如果您运行像这样的同步代码 f = app.loop.run_in_executor(None, your_sync_function, app,param1,param2,...)

然后your_sync_function在单独的线程中运行,您可以执行time.sleep()而不会打扰asyncio循环。它阻止循环执行器的线程,但不阻止异步线程。至少,这似乎是这样做的。

如果您想将消息从your_sync_function发送回asyncio的循环,请查看janus

关于此的更多提示:

https://carlosmaniero.github.io/asyncio-handle-blocking-functions.html