在asyncio中链接协程(和观察者模式)

时间:2015-03-15 20:05:21

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

我无法将协同程序链接在一起。在一个比hello world或factorial稍微简单的例子中,我希望有一个循环不断监视文件修改时间,然后在触摸文件时打印出时间:

#!/usr/bin/env python3
import os
import asyncio

@asyncio.coroutine
def pathmonitor(path):
    modtime = os.path.getmtime(path)
    while True:
        new_time = os.path.getmtime(path)
        if new_time != modtime:
            modtime = new_time
            yield modtime
        yield from asyncio.sleep(1)

@asyncio.coroutine
def printer():
    while True:
        modtime = yield from pathmonitor('/home/users/gnr/tempfile')
        print(modtime)

loop = asyncio.get_event_loop()
loop.run_until_complete(printer())
loop.run_forever()

我希望这可行 - 但是,当我运行它时,我得到了一个:

RuntimeError: Task got bad yield: 1426449327.2590399

我在这里做错了什么?

更新:请参阅下面的答案,了解观察者模式的示例(即,在文件被触摸时有效地允许多个注册者获取更新),而不使用回调(您必须使用任务)。

UPDATE2:有一个更好的解决方法:3.5' s async for(异步迭代器):https://www.python.org/dev/peps/pep-0492/

2 个答案:

答案 0 :(得分:4)

我在链式协程中使用return代替yield使代码正常工作,就像chained coroutines example

#!/usr/bin/env python3
import os
import asyncio2

@asyncio.coroutine
def pathmonitor(path):
    modtime = os.path.getmtime(path)
    while True:
        new_time = os.path.getmtime(path)
        if new_time != modtime:
            modtime = new_time
            return modtime
        yield from asyncio.sleep(1)


@asyncio.coroutine
def printer():
    while True:
        modtime = yield from pathmonitor('/tmp/foo.txt')
        print(modtime)


loop = asyncio.get_event_loop()
loop.run_until_complete(printer())
loop.run_forever()

请注意,printer()的循环将为每次迭代创建一个新的pathmonitor生成器。不确定这是不是你的想法,但它可能是一个开始。

我发现协程API和语法有点令人困惑。以下是我发现有用的一些阅读材料:

答案 1 :(得分:0)

正如其他人指出的那样,我的错误在于我试图使用像发电机这样的协程。我不需要依赖生成器进行迭代,而是需要创建多个协同程序。此外,我需要使用任务来实现观察者模式而不需要回调,因为多个注册者可以yield from执行相同的任务。我的路径监视器看起来像这样:

import os
import asyncio

class PathInfo:

    def __init__(self, path):
        self.path = path
        self.modtime = os.path.getmtime(path)
        self.startTask()

    def startTask(self):
        self.task = asyncio.async(self._checkIfTouched())

    def _checkIfTouched(self):
        while True:
            yield from asyncio.sleep(1)
            newtime = os.path.getmtime(self.path)
            if self.modtime != newtime:
                self.modtime = newtime
                return newtime

class PathMonitor:

    def __init__(self):
        self._info = {}

    @asyncio.coroutine
    def wasTouched(self, path):
        try:
            info = self._info[path]
        except KeyError:
            self._info[path] = info = PathInfo(path)
        if info.task.done():
            info.startTask()
        modtime = yield from info.task
        return modtime

def printer():
    while True:
        modtime = yield from mon.wasTouched('/tmp/myfile')
        print(modtime)

mon = PathMonitor()

loop = asyncio.get_event_loop()
asyncio.async(printer())
loop.run_forever()