Процессы и потоки в Python (CPython)

Для начала нам нужно разобраться с терминами:

  • процесс (process) - это, грубо говоря, запущенная программа. Процессу выделяются отдельные ресурсы: память, процессорное время
  • поток (thread) - это единица исполнения в процессе. Потоки разделяют ресурсы процесса, к которому они относятся.

Python (а точнее, CPython - реализация, которая используется в книге) оптимизирован для работы в однопоточном режиме. Это хорошо, если в программе используется только один поток. И, в то же время, у Python есть определенные нюансы работы в многопоточном режиме. Связаны они с тем, что CPython использует GIL (global interpreter lock).

GIL не дает нескольким потокам исполнять одновременно код Python. Если не вдаваться в подробности, то GIL можно представить как некий переходящий флаг, который разрешает потокам выполняться. У кого флаг, тот может выполнять работу. Флаг передается либо каждые сколько-то инструкций Python, либо, например, когда выполняются какие-то операции ввода-вывода.

Поэтому получается, что разные потоки не будут выполняться параллельно, а программа просто будет между ними переключаться, выполняя их в разное время. Однако, если в программе есть некое «ожидание»: пакетов из сети, запроса пользователя, пауза типа time.sleep, то в такой программе потоки будут выполняться как будто параллельно. А всё потому, что во время таких пауз флаг (GIL) можно передать другому потоку.

То есть, потоки отлично подходят для задач, которые связаны с операциями ввода-вывода:

  • Подключение к оборудованию и подключение по сети в целом
  • Работа с файловой системой
  • Скачивание файлов по сети

Примечание

В интернете часто можно встретить выражения «В Python лучше вообще не использовать потоки». К сожалению, такие фразы не всегда пишут с контекстом, а именно, что речь о конкретных задачах, которые завязаны на CPU.

В следующих разделах рассматривается, как использовать потоки для подключения по Telnet/SSH. И проверяется, какое суммарное время будет занимать исполнение скрипта, по сравнению с последовательным исполнением и с использованием процессов.

Процессы

Процессы позволяют выполнять задачи на разных ядрах компьютера. Это важно для задач, которые завязаны на CPU. Для каждого процесса создается своя копия ресурсов, выделяется память, у каждого процесса свой GIL. Это же делает процессы более тяжеловесными, по сравнению с потоками.

Кроме того, количество процессов, которые запускаются параллельно, зависит от количества ядер и CPU и обычно исчисляется в десятках, тогда как количество потоков для операций ввода-вывода может исчисляться в сотнях.

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

Примечание

Совмещение потоков и процессов, то есть запуск процесса в программе и внутри него уже запуск потоков - сильно усложняет траблшутинг программы. И лучше такой вариант не использовать.

Несмотря на то, что, как правило, для задач ввода-вывода лучше использовать потоки с некоторыми модулями надо использовать процессы, так как они могут некорректно работать с потоками.

Примечание

Помимо процессов и потоков есть еще один вариант одновременного подключения к оборудованию: асинхронное программирование. Этот вариант не рассматривается в книге.