Как работают потоки в Python, и каковы распространенные ошибки, связанные с потоками Python? - PullRequest
81 голосов
/ 28 августа 2008

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

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

Где есть хорошее объяснение или вы можете его дать? Также было бы очень полезно знать о распространенных проблемах, с которыми вы сталкиваетесь при использовании потоков с Python.

Ответы [ 7 ]

49 голосов
/ 28 августа 2008

Да, из-за Глобальной блокировки интерпретатора (GIL) может выполняться только один поток за раз. Вот несколько ссылок с некоторыми соображениями по этому поводу:

Из последней ссылки интересная цитата:

Позвольте мне объяснить, что все это значит. Потоки работают внутри одного и того же виртуального машина, а значит и бег на той же физическая машина. Процессы могут работать на той же физической машине или в другая физическая машина. если ты разработайте приложение темы, вы ничего не сделали для доступа несколько машин. Таким образом, вы можете масштабировать так много ядер на одном машина (которой будет немало со временем), но чтобы действительно достичь веб весы, вам нужно решить в любом случае проблема с несколькими машинами.

Если вы хотите использовать многоядерный режим, pyprocessing определяет API на основе процессов для реальной распараллеливания. PEP также включает некоторые интересные тесты.

35 голосов
/ 28 августа 2008

Python - довольно простой язык для встраивания, но есть предостережения. Самая важная вещь, о которой вам нужно знать - это Global Interpreter Lock. Это позволяет только одному потоку получить доступ к интерпретатору. Это означает две вещи: 1) вы редко когда-либо сталкивались с оператором блокировки в python и 2) если вы хотите использовать преимущества многопроцессорных систем, вы должны использовать отдельные процессы. РЕДАКТИРОВАТЬ: Я также должен отметить, что вы можете поместить часть кода в C / C ++, если вы хотите обойти GIL.

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

Если вы хотите улучшить отзывчивость, вы должны учитывать потоки. Есть и другие альтернативы, а именно микропоточность . Есть также некоторые фреймворки, которые вы должны изучить:

19 голосов
/ 28 августа 2008

Ниже приведен базовый пример резьбы. Это будет порождать 20 потоков; каждый поток выведет свой номер потока. Запустите его и соблюдайте порядок, в котором они печатаются.

import threading
class Foo (threading.Thread):
    def __init__(self,x):
        self.__x = x
        threading.Thread.__init__(self)
    def run (self):
          print str(self.__x)

for x in xrange(20):
    Foo(x).start()

Как вы уже намекали, потоки Python реализуются с помощью среза времени. Так они получают «параллельный» эффект.

В моем примере мой класс Foo расширяет поток, затем я реализую метод run, в который и поступает код, который вы хотели бы запустить в потоке. Чтобы запустить поток, вы вызываете start() для объекта потока, который автоматически вызывает метод run ...

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

10 голосов
/ 28 августа 2008

Используйте потоки в python, если отдельные работники выполняют операции, связанные с вводом / выводом. Если вы пытаетесь масштабировать несколько ядер на машине, либо найдите хорошую IPC среду для Python, либо выберите другой язык.

4 голосов
/ 30 марта 2018

Примечание: везде, где я упоминаю thread, я имею в виду конкретно потоков в питоне до тех пор, пока не будет указано явно.

Потоки работают немного по-другому в Python, если вы пришли из C/C++ фона. В python только один поток может быть в рабочем состоянии в определенный момент времени. Это означает, что потоки в python не могут по-настоящему использовать возможности нескольких процессорных ядер, так как по своей конструкции потоки не могут работать параллельно на нескольких ядрах.

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

Why does python use GIL?

Для предотвращения одновременного доступа нескольких потоков к состоянию интерпретатора и повреждения состояния интерпретатора.

Идея состоит в том, что всякий раз, когда выполняется поток (даже если это основной поток) , получается GIL и через некоторый предопределенный интервал времени GIL освобождается текущим потоком и повторно запрашивается другим потоком (если есть).

Why not simply remove GIL?

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

, поэтому стоимость удаления GIL компенсируется снижением производительности однопоточного приложения, что никогда не требуется.

So when does thread switching occurs in python?

Переключение нити происходит при отпускании GIL. Так, когда GIL выпущен? Необходимо принять во внимание два сценария.

Если поток выполняет операции привязки к процессору (например, обработка изображений).

В более старых версиях python переключение потоков происходило после фиксированного количества инструкций python. По умолчанию было установлено значение 100. Оказалось, что это не очень хорошая политика для принятия решения. когда переключение должно произойти, так как время, затрачиваемое на выполнение одной инструкции, может очень дико от миллисекунды до даже секунды. Поэтому выпуск GIL после каждых 100 инструкций независимо от времени, которое они требуют для выполнения, является плохой политикой.

В новых версиях вместо использования счетчика команд в качестве метрики для переключения потока используется настраиваемый интервал времени. Интервал переключения по умолчанию составляет 5 миллисекунд. Вы можете получить текущий интервал переключения, используя sys.getswitchinterval(). Это можно изменить с помощью sys.setswitchinterval()

Если поток выполняет некоторые операции ввода-вывода (например, доступ к файловой системе или
) сетевой ввод / вывод)

GIL освобождается всякий раз, когда поток ожидает некоторого завершения операции ввода-вывода.

Which thread to switch to next?

Интерпретатор не имеет своего собственного планировщика. Какой поток становится запланированным в конце интервала, это решение операционной системы. .

3 голосов
/ 06 февраля 2014

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

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

2 голосов
/ 29 июля 2009

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

Я бы даже посоветовал использовать несколько процессоров для родителей и попытался бы сохранить одинаковые рабочие места на одном и том же ядре.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...