使用asyncio

时间:2015-11-20 10:30:04

标签: python python-asyncio

我希望在编写日志文件时阅读它们并使用asyncio处理它们的输入。代码必须在Windows上运行。根据我在搜索stackoverflow和web时的理解,异步文件I / O在大多数操作系统上都很棘手(例如,select将无法正常工作)。虽然我确信我可以用其他方法(例如线程)做到这一点,但我会尝试使用asyncio来查看它是什么样的。最有用的答案可能是描述这个问题的解决方案的“架构”应该是什么样的,即应该如何调用或调度不同的函数和协程。

以下为我提供了一个逐行读取文件的生成器(通过轮询,这是可以接受的):

import time

def line_reader(f):
    while True:
        line = f.readline()
        if not line:
            time.sleep(POLL_INTERVAL)
            continue
        process_line(line)

要监视和处理多个文件,这种代码需要线程。我稍微修改了它以便更适用于asyncio:

import asyncio

def line_reader(f):
    while True:
        line = f.readline()
        if not line:
            yield from asyncio.sleep(POLL_INTERVAL)
            continue
        process_line(line)

当我通过asyncio事件循环安排它时,这种方法有效,但如果process_data阻塞,那么这当然不好。在开始时,我想象解决方案看起来像

def process_data():
    ...
    while True:
        ...
        line = yield from line_reader()
        ...

但我无法弄清楚如何做到这一点(至少没有process_data管理相当多的状态)。

关于如何构建这种代码的任何想法?

4 个答案:

答案 0 :(得分:21)

使用aiofiles

async with aiofiles.open('filename', mode='r') as f:
    async for line in f:
        print(line)

编辑1

正如@Jashandeep所提到的,你应该关心阻止操作:

另一种方法是select和/或epoll

from select import select

files_to_read, files_to_write, exceptions = select([f1, f2], [f1, f2], [f1, f2], timeout=.1)

此处timeout参数很重要。

请参阅:https://docs.python.org/3/library/select.html#select.select

编辑2

您可以使用以下命令注册文件以进行读/写:loop.add_reader()

它在循环内部使用内部EPOLL处理程序。

编辑3

但请记住,Epoll不适用于常规文件。

答案 1 :(得分:15)

  

根据我在搜索stackoverflow和web时的理解,异步文件I / O在大多数操作系统上都很棘手(例如,select不会按预期工作)。虽然我确信我可以用其他方法(例如线程)来做到这一点,但我会尝试使用asyncio来查看它是什么样的。

asyncio select基于* nix系统,因此您无法在没有阻止的情况下执行非阻塞文件I / O使用线程。在Windows上,asyncio可以使用支持非阻止文件I / O的IOCP,但asyncio不支持此功能。

您的代码很好,除非您应该在线程中阻止I / O调用,这样如果I / O很慢,您就不会阻止事件循环。幸运的是,使用loop.run_in_executor函数将工作卸载到线程非常简单。

首先,为您的I / O设置专用线程池:

from concurrent.futures import ThreadPoolExecutor
io_pool_exc = ThreadPoolExecutor()

然后只需将任何阻塞I / O调用卸载到执行程序:

...
line = yield from loop.run_in_executor(io_pool_exc, f.readline)
...

答案 2 :(得分:3)

您的代码结构对我来说很好,以下代码在我的机器上正常运行:

import asyncio

PERIOD = 0.5

@asyncio.coroutine
def readline(f):
    while True:
        data = f.readline()
        if data:
            return data
        yield from asyncio.sleep(PERIOD)

@asyncio.coroutine
def test():
    with open('test.txt') as f:
        while True:
            line = yield from readline(f)
            print('Got: {!r}'.format(line))

loop = asyncio.get_event_loop()
loop.run_until_complete(test())

答案 3 :(得分:-1)

asyncio还不支持文件操作,抱歉。

因此它无法解决您的问题。