Думаю, ни для кого не секрет то, что основная реализация языка программирования Python фактически не поддерживает многопоточности. Есть модули которые позволяют эмулировать потоки посредствам процессов, но подобный путь крайне требователен к ресурсам и поэтому его применимость крайне ограниченна, особенно для большого количества операций ввода/вывода. При этом, в подавляющем большинстве случаев, распараллеливание задач не несет какого-то серьезного практического смысла и просто является одним из возможных архитектурных решений. В качестве альтернативы потокам могут выступать асинхронные операции, а с учетом ограничений интерпретатора, подобный подход должен бы был быть родным подходом в Python уже много лет как. Тем не менее, появился долгожданный модуль asyncio только в Python 3.4, но это в любом случае лучше чем никогда.
Говорить о asyncio в чистом виде не очень интересно, т.к. в отличие от того же Twisted, это низкоуровневое API позволяющее работать с потоками ввода/вывода и блокировками. К счастью, прогресс не стоит на месте и уже имеется как минимум одна интересная библиотека для неблокирующей работы с протоколом HTTP – aiohttp, доступная через pip.
import aiohttp
@asyncio.coroutine #(1)
def load(url, sem):
with (yield from sem): #(2)
response = yield from aiohttp.request('GET', url) #(3)
return (yield from response.read_and_close(decode=True)) #(4)
@asyncio.coroutine #(5)
def get_page(page, sem):
url = "http://example.com/{}".format(page)
text = yield from load(url, sem) #(6)
print("URL: {}, TEXT: {}".format(url ,text[:20]))
def main():
loop = asyncio.get_event_loop() #(7)
sem = asyncio.Semaphore(10) #(8)
requests = [get_page("page_{}".format(x), sem) for x in range(1000)]
f = asyncio.wait(requests) #(9)
loop.run_until_complete(f) #(10)
if __name__ == "__main__":
main()
Работа с asyncio особо не отличается от работы с библиотеками асинхронного IO на любом другом языке программирования за исключением встроенной поддержки coroutine. Конечно, возможности предоставляемы coroutine можно и не использовать, но на мой взгляд они сильно облегчают написание и поддержку не сложных асинхронных приложений, хотя и не подходят, с архитектурной точки зрения, для чего-то большого, требующего полного контроля над операциями ввода/вывода.
Ядром любой асинхронной IO библиотеки является обработчик сообщений, и asyncio тут не исключение. Так метод
Библиотека asyncio поддерживает большое количество различных способов обработки событий на все случаи жизни. А в моем примере я просто ограничился функцией
Принцип работы функций являющихся coroutine
В качестве небольшого дополнения к примеру был добавлен семафор
Уверен, что количество библиотек содержащих реализации новых сетевых протоколов поверх asyncio в скором времени будет стремительно расти. Хотя я, в первую очередь, надеюсь что это событие послужит толчком, который ускорит портирование Twisted на Python3.
Если верить документации, то они поддерживают файловый асинхрнонный IO тоже
http://docs.python.org/dev/library/asyncio-eventloop.html#watch-file-descriptors.
Я не множко посмотрел внутри, там все тот же kqueue и epoll, который всегда будет говорить, что файл готов для чтения и записи.
Я что то не уловил?
PS использую ruby, не python, смотрел из интереса.
Да, относительно kqueue и epoll все верно. Но в той же Windows может использоваться IOCP, что уже даст неблокирующий асинхронный IO, а для *NIX есть AOI. Судя по документации к asyncio, на данный момент они реализовали поддержку только IOCP, т.е. на *NIX используется блокирующий асинхронный IO.
По мне так это все-таки блокирующий синхронный IO (для файлов).
А где про IOCP написано?
Ну, официально это называется блокирующий асинхронный IO
Про IOCP можно найти в PEP-3156, в разделе “Event Loop Classes”.
Все-таки неблокирующий I/O, так как без него, нет никакого смысла, ввиду того, что event loop работает в одном потоке с курутинами и если в курутине заблокировать I/O каким-нить блокирующим вызовом, например: чтение из сокета, который небыл переведен в non-bloking mode, то заблокируется весь тред, вместе с event loop. Для этого даже sleep() сделали в виде курутины (yield from async.sleep()), а не встроенный блокирующий time.sleep().
P.S. Python, как язык, не имеет никаких ограничений на использование потоков и процессов, более того, потоки не green thread, а реально системные треды, которые скедулятся осью. Проблема в GIL, который в имплементации языка Python — интерпретаторе CPython, если без нее, то PyPy…