Фон

Python предлагает несколько подходов к параллелизму и параллелизму.

Двумя наиболее популярными способами параллельного выполнения в Python являются многопоточность и многопроцессорность. Хотя оба метода направлены на повышение производительности программы Python, они различаются по своему подходу и вариантам использования.

В этом сегменте я расскажу, когда следует использовать многопоточность и/или многопроцессорность в Python для достижения хороших и производительных результатов кода.

Многопоточность

Многопоточность — это метод, при котором один процесс или программа делится на несколько потоков выполнения.

Каждый поток выполняется одновременно, используя одно и то же пространство памяти и системные ресурсы. В Python многопоточность реализуется с помощью модуля threading.

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

Однако многопоточность может не подходить для задач, связанных с ЦП, таких как математические вычисления, обработка изображений и рендеринг видео, где узким местом является время ЦП. Это связано с тем, что Python использует глобальную блокировку интерпретатора (GIL), которая предотвращает одновременное выполнение байт-кода Python несколькими потоками.

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

Вот пример многопоточности в Python:

import threading

def print_numbers():
    for i in range(10):
        print(i)

def print_letters():
    for letter in 'abcdefghij':
        print(letter)

thread1 = threading.Thread(target=print_numbers)
thread2 = threading.Thread(target=print_letters)

thread1.start()
thread2.start()

thread1.join()
thread2.join()

print("Done")

В этом примере у нас есть две функции: print_numbers() и print_letters(), каждая из которых выполняется в отдельном потоке.

Класс Thread из модуля threading используется для создания двух потоков, каждый из которых предназначен для одной из двух функций. Метод start() вызывается в каждом потоке, чтобы начать выполнение, а метод join() вызывается в каждом потоке, чтобы дождаться их завершения. Наконец, программа печатает «Готово», чтобы указать, что оба потока завершили выполнение.

Многопроцессорность

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

Каждый процесс имеет собственный интерпретатор и виртуальную машину Python (PVM) и может выполнять код Python параллельно на разных ядрах ЦП. В Python многопроцессорность реализуется с помощью модуля multiprocessing.

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

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

Вот пример многопроцессорности в Python:

import multiprocessing

def square(number):
    return number * number

if __name__ == '__main__':
    numbers = [1, 2, 3, 4, 5]
    pool = multiprocessing.Pool(processes=3)

Краткое содержание

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

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

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

Кроме того, важно отметить, что глобальная блокировка интерпретатора Python (GIL) предотвращает одновременное выполнение байт-кода Python несколькими потоками. В результате многопоточность может не подходить для задач, связанных с ЦП, которые требуют параллелизма, и в таких случаях предпочтительнее использовать многопроцессорность.