Общая информация
Однажды я работал над системой Python 2, в которой было много пользовательского кода ввода-вывода, написанного синхронно и масштабируемого с помощью потоков. В какой-то момент мы не смогли масштабировать его дальше и поняли, что нам нужно перейти на асинхронное программирование.
Twisted был популярным выбором, но мы хотели избежать его ада обратных вызовов.
У него был декоратор @inlineCallbacks, который эффективно реализовывал сопрограммы с помощью магии генератора, как и некоторые другие библиотеки. Это было более терпимо, но было немного нестабильно.
А потом мы нашли гевент. Все, что вам нужно было сделать, это:
И точно так же весь ваш стандартный ввод-вывод — сокеты, транзакции базы данных, все, что на самом деле написано на чистом Python — был асинхронным, давал и переключился за кулисами с использованием гринлетов.
Это было не идеально:
В то время это не очень хорошо работало в Windows (и до сих пор имеет некоторые ограничения). К счастью, мы работали в Linux.
Он не мог использовать расширения C, поэтому мы не могли использовать, например, MySQLdb. К счастью, существовало множество чистых альтернатив Python, таких как PyMySQL.
Вопрос
В настоящее время Python 3 гораздо более популярен, а вместе с ним и asyncio. Лично я считаю, что это здорово, но недавно меня спросили, чем это отличается от того, что мы реализовали с помощью gevent, и я не смог дать достаточно хорошего ответа.
Это может звучать субъективно, но на самом деле я ищу реальные варианты использования, где один значительно превосходит другой или допускает что-то, чего другой нет. Вот соображения, которые я собрал на данный момент:
Как я уже сказал, gevent довольно ограничен в Windows. Опять же, большая часть известного мне производственного кода работает в Linux. Если вам нужно работать в Windows, используйте asyncio.
Gevent не может автоматически исправлять расширения C. Но asyncio не может исправить что-либо.
Представьте, что появилась новая технология баз данных, и вы хотели бы ее использовать, но для нее не существует чистой библиотеки Python, поэтому вы не можете интегрировать ее с Gevent. Дело в том, что вы застряли, даже если нет библиотеки io*, которую можно было бы интегрировать с asyncio! Конечно, есть рабочие потоки и исполнители, но дело не в этом, и в любом случае они работают одинаково хорошо в обоих случаях.
Некоторые люди говорят, что это вопрос личного вкуса, но я думаю, будет справедливо сказать, что синхронное программирование по своей сути проще, чем асинхронное (задумайтесь: встречали ли вы когда-нибудь начинающего программиста, который может работать с сокетами, но которому трудно понять, как правильно выбирать/опросить их или думать о будущем/обещаниях, а встречали ли вы когда-нибудь обратное?).
В любом случае, не будем туда заходить. Я хотел затронуть этот вопрос, потому что он часто возникает (вот обсуждение на Reddit), но на самом деле меня интересуют сценарии, в которых у вас есть практическая причина использовать тот или иной вариант.
Asyncio является частью стандартной библиотеки. Это очень важно: это означает, что он хорошо поддерживается, хорошо документирован, и все знают о нем и используют его по умолчанию.
Но, учитывая, как мало вам нужно знать о Gevent, чтобы его использовать (а также то, что он довольно хорошо поддерживается и документирован), это не кажется таким уж важным. Таким образом, несмотря на то, что в Stack Overflow есть множество ответов даже для самых сложных сценариев, связанных с фьючерсами, возможность вообще не использовать фьючерсы кажется столь же жизнеспособной.
Конечно, у Гвидо и сообщества Python была веская причина вложить столько усилий в Asyncio и даже ввести в языки новые ключевые слова. Кажется, я просто не могу их найти.
Каковы ключевые различия между ними и в каких сценариях они становятся очевидными?
Общая информация Однажды я работал над системой Python 2, в которой было много пользовательского кода ввода-вывода, написанного синхронно и масштабируемого с помощью потоков. В какой-то момент мы не смогли масштабировать его дальше и поняли, что нам нужно перейти на асинхронное программирование. [list] [*]Twisted был популярным выбором, но мы хотели избежать его ада обратных вызовов. [*]У него был декоратор @inlineCallbacks, который эффективно реализовывал сопрограммы с помощью магии генератора, как и некоторые другие библиотеки. Это было более терпимо, но было немного нестабильно. [*]А потом мы нашли гевент. Все, что вам нужно было сделать, это: [/list] [code]from gevent import monkey monkey.patch_all() [/code] И точно так же весь ваш стандартный ввод-вывод — сокеты, транзакции базы данных, все, что на самом деле написано на чистом Python — был асинхронным, давал и переключился за кулисами с использованием гринлетов. Это было не идеально: [list] [*]В то время это не очень хорошо работало в Windows (и до сих пор имеет некоторые ограничения). К счастью, мы работали в Linux. [*]Он не мог использовать расширения C, поэтому мы не могли использовать, например, MySQLdb. К счастью, существовало множество чистых альтернатив Python, таких как PyMySQL. [/list] Вопрос В настоящее время Python 3 гораздо более популярен, а вместе с ним и asyncio. Лично я считаю, что это здорово, но недавно меня спросили, чем это отличается от того, что мы реализовали с помощью gevent, и я не смог дать достаточно хорошего ответа. Это может звучать субъективно, но на самом деле я ищу [b]реальные варианты использования[/b], где один значительно превосходит другой или допускает что-то, чего другой нет. Вот соображения, которые я собрал на данный момент: [list] [*]Как я уже сказал, gevent довольно ограничен в Windows. Опять же, большая часть известного мне производственного кода работает в Linux. [b]Если вам нужно работать в Windows, используйте asyncio[/b].
[*]Gevent не может автоматически исправлять расширения C. Но asyncio не может исправить что-либо. Представьте, что появилась новая технология баз данных, и вы хотели бы ее использовать, но для нее не существует чистой библиотеки Python, поэтому вы не можете интегрировать ее с Gevent. Дело в том, что вы застряли, даже если нет библиотеки io*, которую можно было бы интегрировать с asyncio! Конечно, есть рабочие потоки и исполнители, но дело не в этом, и в любом случае они работают одинаково хорошо в обоих случаях.
[*]Некоторые люди говорят, что это вопрос личного вкуса, но я думаю, будет справедливо сказать, что синхронное программирование по своей сути проще, чем асинхронное (задумайтесь: встречали ли вы когда-нибудь начинающего программиста, который может работать с сокетами, но которому трудно понять, как правильно выбирать/опросить их или думать о будущем/обещаниях, а встречали ли вы когда-нибудь обратное?). В любом случае, не будем туда заходить. Я хотел затронуть этот вопрос, потому что он часто возникает (вот обсуждение на Reddit), но на самом деле меня интересуют сценарии, в которых у вас есть практическая причина использовать тот или иной вариант.
[*]Asyncio является частью стандартной библиотеки. Это очень важно: это означает, что он хорошо поддерживается, хорошо документирован, и все знают о нем и используют его по умолчанию. Но, учитывая, как мало вам нужно знать о Gevent, чтобы его использовать (а также то, что он довольно хорошо поддерживается и документирован), это не кажется таким уж важным. Таким образом, несмотря на то, что в Stack Overflow есть множество ответов даже для самых сложных сценариев, связанных с фьючерсами, возможность вообще не использовать фьючерсы кажется столь же жизнеспособной.
[/list] Конечно, у Гвидо и сообщества Python была веская причина вложить столько усилий в Asyncio и даже ввести в языки новые ключевые слова. Кажется, я просто не могу их найти. Каковы ключевые различия между ними и в каких сценариях они становятся очевидными?