Сравнение асинхронного программирования и потоков#
В Python есть три основных способа запускать задачи «параллельно»:
процессы
потоки
асинхронное программирование
Примечание
Полноценно параллельная работа будет только при использовании процессов, по всех остальных случая речь идет о попеременном выполнении (concurrent).
Модули, которые позволяют запустить задачи:
в процессах: multiprocessing, concurrent.futures
в потоках: threading, concurrent.futures
асинхронно: asyncio и масса других вариантов и сторонних фреймворков
Примечание
Для упрощения я буду говорить о запуске функций в потоках/процессах, но технически это может быть любой вызываемый объект. Аналогично дальше я в основном пишу asyncio, но почти все из сказанного про asyncio относится и к другим вариантам асихронного программирования.
Запуск функций в процессах это единственный вариант из перечисленных выше, который позволит запустить код на разных ядрах. При этом, когда речь идет о количестве процессов это единицы-десятки. Если задача завязана на CPU это единственный способ как-то распараллелить задачу.
Потоки в CPython выполняются не параллельно, а попеременно из-за GIL. Поэтому преимущества при использовании потоков будут только для задач, которые завязаны на ввод-вывод. Например, в книге по основам Python мы рассматривали именно потоки, потому что подключение к оборудованию это задача завязанная на операции ввода-вывода. Количество параллельных потоков может быть десятки-сотни.
Асинхронное программирование это еще один вариант выполнения задач попеременно. В Python есть много способов реализации асинхронности, но в книге рассматривается только один - asyncio. Как подсказывает название, asyncio также предназначен для задач завязанных на операции ввода-вывода (IO). В этом подходе все выполняется в одном потоке, но при этом можно делать тысячи параллельных подключений.
Примечание
Для знакомства с другими вариантами реализации асинхронности очень рекомендую послушать Олега Молчанова.
Часто можно встретить фразу, что асинхронный код проще чем многопоточный. Тут имеется в виду, что в асинхронном коде очень четко видно те места, где будет переключение, а весь остальной код будет выполняться последовательно без прерывания. В то время как многопоточный код может быть прерван в любом месте и часто это приводит к проблемам.
При этом разобраться с тем как работает asyncio совсем не просто. К тому же, в большинстве случаев не получится использовать уже известные модули, например, netmiko, а вместо них надо будет использовать асинхронный аналог.
Самые большие преимущества при использовании asyncio можно получить, например, в веб. Тут подключений действительно может быть тысячи и с asynio не надо будет создавать поток для каждого подключения. Однако для работы с сетевым оборудованием asyncio тоже может быть полезен и во многих случаях, особенно когда подключений много, работать быстрее многопоточного варианта.
При этом конечно надо учитывать, что почти все модули, которые работали в многопоточном варианте, не будут работать с asyncio, так как они будут блокировать работу и не будут отдавать управление циклу событий.
Отличия asyncio от многопоточной работы:
при работе с потоками, планировщик может прервать работу потока в любой момент, не всегда это «удобный» момент, поэтому надо явно указывать, что в какие-то моменты прерывать поток нельзя
при работе с asyncio, мы явно указываем в каком месте надо сделать переключение (await)
при использовании asyncio работа идет в одном потоке
в потоках будет работать почти любой из распространенных модулей
для работы с asyncio, как правило, надо будет искать альтернативные модули