2015-05-03 3 views
1

Это скорее концептуальный вопрос, и я пытаюсь понять свое право на то, как совместное существование async IO и потоков.Asynchronous IO и Threads

У многих libs/frameworks есть ограничение, которое, даже если они поддерживают операции async, «другие» библиотеки, от которых они зависят, по-прежнему блокируются.

Например, Tornado - это знаменитая асинхронная веб-инфраструктура Python и сетевая библиотека. Но, интегрируя его с другими, вы должны убедиться, что они также асинхронны. Таким образом, вы не должны использовать redis.py, например, с торнадо, потому что он блокирует и не даст вам асинхронную доброту.

Node.js, с другой стороны, имеет большое преимущество в том, что все асинхронно, и поэтому нет проблем с тем, чтобы иметь дело со смесью асинхронных и блокирующих библиотек.

Теперь, мой вопрос: мы не можем решить проблему смешивания асинхронных библиотек и блокировки потоками? Мы можем просто делать блокирующие вызовы в отдельных потоках, а затем уведомлять о главном цикле асинхронов, когда заканчивается вызов блокировки?

Следовательно, это не «обертывание» блокирующего вызова в потоке, а затем использование какой-то конструкции wait/notify, по существу превращающей этот блокирующий вызов в асинхронный вызов?

Мое понимание того, как потоки играют с асинхронным кодом, все еще примитивно, поэтому, пожалуйста, исправьте любые заблуждения, которые могут возникнуть по этому поводу.

ответ

0

Следовательно, это не «обертывание» блокирующего вызова в потоке, а затем использование какой-либо конструкции wait/notify, по существу превращающей этот блокирующий вызов в асинхронный вызов?

Это именно то, что libuv (базовая основа node.js) делает для API-интерфейсов, которые не имеют неблокированный аналог (например, операций с файловой системой).

Он использует thread pool для выполнения этих блокирующих операций и уведомляет основной цикл событий после того, как они закончили:

libuv обеспечивает ThreadPool, который может быть использован для запуска кода пользователя и получить уведомление в потоке петли , Этот пул потоков внутренне используется для запуска всех операций с файловой системой, а также запросов getaddrinfo и getnameinfo.

0

Ваше предложение об упаковке делает звук намного проще, чем на практике. Для таких языков, как python, выполнение этого и создание отдельного потока выполнения, который имеет обратные вызовы для родительского потока, может быть трудно достигнуть, особенно если вы редактируете существующую кодовую базу, которая никогда не предназначалась для такой упаковки.

Я видел это с некоторыми модулями в node.js. Например, модуль crypto не имеет асинхронных реализаций (еще (я считаю, что есть планы изменить это)), но путем нереста дочерних процессов вы можете эмулировать это с помощью обратных вызовов. Конкретная реализация этого пакета, который я видел в действии, не обеспечила заметного улучшения производительности, поэтому, возможно, поэтому вы не заметили, что это много сделано для библиотек, которые изначально были реализованы синхронно.

+0

Так что это означает, что в среде async потоки бесполезны? Что означает, что асинхронная программа всегда выигрывает от однопоточной? (например, Nginx) – treecoder

+0

@treecoder Я этого не говорил. Темы невероятно полезны, даже в асинхронных средах. Процессы Node.js _intended_ запускаются параллельно, чтобы масштабировать производство, но если вы попытаетесь использовать потоки для изменения синхронного процесса на асинхронный, тогда вы должны начать сомневаться в фактической выгоде от этого. –

+0

Итак, синхронный код не должен превращаться в асинхронный с потоками. Правильный способ - всегда использовать epoll/kqueue и семью. Я прав? И если да, то на языках более высокого уровня, таких как python, для этой цели мы должны использовать такие вещи, как новый модуль 'asyncio'? – treecoder

0

Основная проблема с потоками заключается в том, что каждая нить потребляет много памяти ядра (до 1 МБ), а неограниченный параллелизм приводит к исчерпанию памяти. Async I/O позволяет каждому соединению занимать ограниченную часть памяти, поэтому позволяет увеличить количество одновременных подключений. Цена - это ограничение для соединений/задач для выполнения операций блокировки, иначе это приведет к исчерпанию памяти.

Итак, наша главная цель - ограничить потребление памяти для стеков потоков. Правильно, как вы предложили использовать выделенный поток для блокировки операций. Мы также можем думать о других способах, как использование пула одиночных потоков с подсчетом и ограничение количества одновременных задач блокировки, но они более сложны и вряд ли более эффективны.