【问题标题】:What File Descriptor object does Python AsyncIO's loop.add_reader() expect?Python AsyncIO 的 loop.add_reader() 期望什么文件描述符对象?
【发布时间】:2014-08-17 17:55:11
【问题描述】:

我正在尝试了解如何在 Python 3.4 中使用新的 AsyncIO 功能,但我正在努力解决如何使用 event_loop.add_reader() 的问题。从limited discussions 我发现它看起来像是从一个单独的进程中读取标准而不是打开文件的内容。真的吗?如果是这样,似乎没有 AsyncIO 特定的方式来集成标准文件 IO,这也是真的吗?

我一直在玩下面的代码。下面的输出给出了 /python3.4/selectors.py self._epoll.register(key.fd, epoll_events) 的第 399 行中的 PermissionError: [Errno 1] Operation not permitted 异常,该异常由下面的 add_reader() 行触发

import asyncio
import urllib.parse
import sys
import pdb
import os

def fileCallback(*args):
    pdb.set_trace()

path = sys.argv[1]
loop = asyncio.get_event_loop()
#fd = os.open(path, os.O_RDONLY)
fd = open(path, 'r')
#data = fd.read()
#print(data)
#fd.close()
pdb.set_trace()
task = loop.add_reader(fd, fileCallback, fd)
loop.run_until_complete(task)
loop.close()

编辑

对于那些正在寻找如何使用 AsyncIO 一次读取多个文件的示例的人,我很好奇,这里有一个如何实现的示例。秘密在yield from asyncio.sleep(0) 行中。这实质上会暂停当前函数,将其放回事件循环队列中,以便在所有其他就绪函数执行后调用。函数根据它们的调度方式确定是否准备就绪。

import asyncio

@asyncio.coroutine
def read_section(file, length):
    yield from asyncio.sleep(0)
    return file.read(length)

@asyncio.coroutine
def read_file(path):
    fd = open(path, 'r')
    retVal = []
    cnt = 0
    while True:
        cnt = cnt + 1
        data = yield from read_section(fd, 102400)
        print(path + ': ' + str(cnt) + ' - ' + str(len(data)))
        if len(data) == 0:
            break;
    fd.close()

paths = ["loadme.txt", "loadme also.txt"]
loop = asyncio.get_event_loop()
tasks = []
for path in paths:
    tasks.append(asyncio.async(read_file(path)))
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

【问题讨论】:

  • 请参阅this question 了解失败的原因; epoll 不支持常规文件。
  • @dano:如果我在 FreeBSD 上这样做,它会使用 kqueue 并处理常规文件吗?
  • 我不确定,但我知道 AsyncIO 的目的是公开文件系统的标准 IO 回调。因此,如果这是 FreeBSD 执行 IO 回调的标准方式,那么可能
  • @JanusTroelsen 在 FreeBSD 上使用 SelectorEventLoop,它使用 selectors 模块为平台选择最有效的事件循环。如果那是 kqueue,那么这应该是 selectors 选择的。不过,我不知道这是否会使add_reader 与常规文件一起使用。如果你试一试,请告诉我进展如何!

标签: python file-io python-3.4 python-asyncio


【解决方案1】:

这些函数需要一个文件描述符,即操作系统使用的底层整数,而不是 Python 的文件对象。基于文件描述符的文件对象在 fileno() 方法上返回该描述符,例如:

>>> sys.stderr.fileno()
2

在 Unix 中,文件描述符可以附加到文件或许多其他东西上,包括其他进程。

为 OP 的编辑编辑:

正如 cmets 中的 Max 所说,您不能在本地文件上使用 epoll(而 asyncio 使用 epoll)。是的,这有点奇怪。不过,您可以在管道上使用它,例如:

import asyncio
import urllib.parse
import sys
import pdb
import os

def fileCallback(*args):
    print("Received: " + sys.stdin.readline())

loop = asyncio.get_event_loop()
task = loop.add_reader(sys.stdin.fileno(), fileCallback)
loop.run_forever()

这将回显您在标准输入上编写的内容。

【讨论】:

  • 好的,那么在我的示例中,返回数字文件描述符的os.open() 应该可以工作吗?因为它给了我同样的结果
  • 本地文件通常不能被选择/轮询等,因为它们不会阻塞。
  • 更新了我的答案以反映您更新的问题:-)
  • 是不是因为本地加载文件效率高?如果您有一个或多个需要加载的大文件,您会遇到阻塞吗?
  • 是的,如果一次将一个大文件全部读入进程内存需要太长时间,您可以读取较小的块(os.read 有一个 buffersize 参数)并在这些块之间产生.
【解决方案2】:

您不能在本地文件上使用 add_reader,因为:

  • 无法使用 select/poll/epoll 完成
  • 这取决于操作系统
  • 由于操作系统限制不能完全异步(linux不支持异步fs元数据读/写)

但是,从技术上讲,是的,您应该能够进行异步文件系统读/写,(几乎)所有系统都具有用于“在后台”进行 i/o 的 DMA 机制。不,本地 i/o 真的并不快,以至于没有人想要它,CPU 的速度是磁盘 i/o 的数百万倍。

如果您想尝试异步 i/o,请查找 aiofile 或 aiofiles

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-13
    • 1970-01-01
    • 1970-01-01
    • 2016-12-21
    • 1970-01-01
    • 2013-03-20
    • 1970-01-01
    • 2015-01-04
    相关资源
    最近更新 更多