Какой объект файлового дескриптора ожидает loop.add_reader() Python AsyncIO?

Я пытаюсь понять, как использовать новую функциональность AsyncIO в Python 3.4, и мне сложно понять, как использовать event_loop.add_reader(). Из ограниченных обсуждений, которые я обнаружил, похоже, что это чтение стандарта из отдельный процесс, а не содержимое открытого файла. Это правда? Если это так, то кажется, что нет специального способа AsyncIO для интеграции стандартного файлового ввода-вывода, верно ли это?

Я играл со следующим кодом. Вывод следующего дает исключение PermissionError: [Errno 1] Operation not permitted из строки 399 /python3.4/selectors.py self._epoll.register(key.fd, epoll_events), которое запускается строкой 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()

person Josh Russo    schedule 17.08.2014    source источник
comment
См. этот вопрос, почему это терпит неудачу; epoll не поддерживает обычные файлы.   -  person dano    schedule 17.08.2014
comment
@dano: Если бы я сделал это во FreeBSD, будет ли он использовать kqueue и работать с обычными файлами?   -  person Janus Troelsen    schedule 17.05.2016
comment
Я точно не уверен, но я знаю, что цель AsyncIO — раскрыть стандартные обратные вызовы ввода-вывода файловой системы. Так что, если это стандартный способ, которым FreeBSD выполняет обратные вызовы ввода-вывода, то, вероятно,   -  person Josh Russo    schedule 17.05.2016
comment
@JanusTroelsen Во FreeBSD используется SelectorEventLoop, который использует selectors для выбора наиболее эффективного цикла обработки событий для платформы. Если это kqueue, то это должно быть то, что выбирает selectors. Я не знаю, заставит ли это add_reader работать с обычными файлами. Если вы попробуете, дайте мне знать, как это идет!   -  person dano    schedule 17.05.2016


Ответы (2)


Эти функции ожидают файловый дескриптор, то есть базовые целые числа, которые использует операционная система, а не файловые объекты Python. Файловые объекты, основанные на файловых дескрипторах, возвращают этот дескриптор в методе fileno(), например:

>>> sys.stderr.fileno()
2

В Unix файловые дескрипторы могут быть прикреплены к файлам или многим другим вещам, включая другие процессы.

Редактировать для редактирования OP:

Как говорит Макс в комментариях, нельзя использовать 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()

Это будет отображать то, что вы пишете на стандартном вводе.

person Jorgen Schäfer    schedule 17.08.2014
comment
Хорошо, тогда в моем примере должен работать os.open(), который возвращает числовой файловый дескриптор? Потому что это дает мне тот же результат - person Josh Russo; 17.08.2014
comment
Локальные файлы обычно не могут быть выбраны/опрошены и т. д., потому что они не блокируются. - person Max; 17.08.2014
comment
Обновлен мой ответ, чтобы отразить ваш обновленный вопрос :-) - person Jorgen Schäfer; 17.08.2014
comment
Это только из-за эффективности загрузки файлов локально? Если бы у вас был большой файл или файлы, которые нужно было загрузить, вы бы столкнулись с блокировкой? - person Josh Russo; 17.08.2014
comment
Хорошо, думаю, я понял. Чтобы сплести большие загрузки файлов в операторе yield from, необходимо ввести его в цикл чтения загрузки файла. Это звучит точно? - person Josh Russo; 17.08.2014
comment
Асинхронный ввод/вывод не выполняет фактический ввод/вывод асинхронно, а позволяет реагировать на доступность ввода (или доступность места для вывода). Вызов read() по-прежнему блокирует процесс во время передачи данных, но поскольку передача — это всего лишь копирование памяти между ядром и пользовательским процессом, это происходит очень и очень быстро. Локальные файлы всегда доступны для чтения, поэтому ситуации, когда чтение из них блокировалось бы, не возникает. Чтение может быть заблокировано, когда, например, сетевой вывод еще недоступен. - person Jorgen Schäfer; 17.08.2014
comment
И да, если одновременное чтение большого файла в память процесса занимает слишком много времени, вы можете выполнять чтение меньшими порциями (у os.read есть аргумент размера буфера) и уступать между этими порциями. - person Jorgen Schäfer; 17.08.2014
comment
В Windows это выдает ошибку OSError [WinError 10038] Попытка операции над чем-то, что не является сокетом. - person blokeley; 20.07.2015

вы не можете использовать add_reader для локальных файлов, потому что:

  • Это невозможно сделать с помощью select/poll/epoll
  • Это зависит от операционной системы
  • Он не может быть полностью асинхронным из-за ограничений ОС (linux не поддерживает асинхронное чтение/запись метаданных fs)

Но технически да, вы должны иметь возможность выполнять асинхронное чтение/запись файловой системы, (почти) все системы имеют механизм DMA для выполнения ввода-вывода «в фоновом режиме». И нет, локальный ввод-вывод не на самом деле быстр, так что он никому не нужен, ЦП порядка миллионов раз быстрее, чем дисковый ввод-вывод.

Ищите aiofile или aiofiles, если вы хотите попробовать асинхронный ввод-вывод.

person Michel    schedule 26.02.2019