Собственно услышал тезис, что "асинхронного файлового IO не существует". Разве это так? Если я правильно понимаю, то вся асинхронность что в сетевом IO, что в файловом строится на опросе буферов\сокетов о наличии данных.
может я сам что не понимаю, но асинхронность это про "не блокирует" (в противовес синхронности - вызывали функцию и ждем), так что реализация не принципиальна: если у вас появляется IoTask в менеджере очереди AsyncManager то это тоже асинхронность когда вы вызываете AsyncManager.AddTask() и передается Action который вызывается когда выполнится действие (и также об ошибке)
Что до нижней реализации - ну допустим оно будет на потоках. Но API то универсальный, разьве нет? Можно подставить другую реализацию если эффективная будет для другой ОС / языка / среды.
Сергей Горностаев
@sergey-gornostaev Куратор тега Асинхронное программирование
Седой и строгий
Кроссплатформенного не существует. Поэтому асинхронный код в Python и Java использует executor'ы для файловых операций, а в Node.js такое же решение скрыто на уровне абстракций libuv.
Разве что zero-copy через уже упомянутый DMA. Но этот метод не всегда применим.
" код в Python и Java использует executor'ы для файловых операций"
А можно поподробнее как это работает?
Написано
Сергей Горностаев
@sergey-gornostaev Куратор тега Асинхронное программирование
beduin01, задача просто передаётся на выполнение в пул потоков, а назад возвращается awaitable объект, который можно асинхронно ждать
import asyncio
import concurrent.futures
def blocking_io():
# File operations (such as logging) can block the
# event loop: run them in a thread pool.
with open('/dev/urandom', 'rb') as f:
return f.read(100)
async def main():
loop = asyncio.get_running_loop()
result = await loop.run_in_executor(None, blocking_io)
print(result)
asyncio.run(main())
Сергей Горностаев, А это только в Питоне есть пул потоков? Я слышал, что в том же C# никакого пула потоков для async операций нет
Написано
Сергей Горностаев
@sergey-gornostaev Куратор тега Асинхронное программирование
beduin01, не могу ничего сказать про C#, писал на нём давно и мало. Возможно, в нём тоже спрятали использование пула потока, как в Node.js. А может C# использует IOCP для асинхронных файловых операций, но тогда они работают только в Windows.
но IOCP и например epoll отличаются настолько сильно, что их почти невозможно объединить общей высокоуровневой абстракцией
Тот же libuv их объединяет.
Правда IOCP в libuv только для сокетов. Файловые операции там реализованы на пуле потоков.
Написано
Сергей Горностаев
@sergey-gornostaev Куратор тега Асинхронное программирование
res2001, вот и я о том же. Я асинхронностью уже много лет обмазываюсь и не могу припомнить ни одной библиотеки, которая реализовывала бы асинхронный файловый ввод/вывод не с помощью потоков. Не особо углублялся в причины, но читал где-то (кажись в python dev-листах), что это обусловлено именно разительными отличиями в реализации файловой асинхронности в разных операционных системах.
Сергей Горностаев, По моему, принципиальных причин для отказа в реализации файлового i/o на iocp или epoll нет (хотя я то же не сильно углублялся).
На мой взгляд, файловые операции реализуют на потоках из-за того, что количество файловых операций в том же libuv гораздо больше, чем открытие/чтение/запись/закрытие.
Получается что либо мы ограничиваем файловый i/o стандартным набором операций (чего явно не достаточно), либо натягиваем весь расширенный интерфейс файловых операций на остальной i/o, который там нафиг не нужен.
Под потоками имеются ввиду системные потоки? Каково их количество и я не совсем понимаю какая альтернатива системным потокам? epoll и тд они же где-то выполняются? Разве не в тех же потоках?
Написано
Сергей Горностаев
@sergey-gornostaev Куратор тега Асинхронное программирование
beduin01, асинхронные механизмы, такие как epoll, выполняются в единственном потоке, не блокируя его. Грубо говоря, крутится бесконечный цикл и на каждой итерации проверяется, какой из дескрипторов сменил состояние. Если какой-то дескриптор готов к чтению, вызываешь неблокируемую операцию чтения и продолжает итерацию. Подробное описание механизмов select, poll и epoll можно почитать например здесь, а про IOCP здесь. На Хабре вообще много статей на эту тему.
На уровне программы безусловно существует. На физическом уровне зависит от системы, обычно да.
Если вы про параллельный io, то, обычно, низший уровень работает последовательно.