Я пытался понять, как потоки работают в Python, и мне трудно найти хорошую информацию о том, как они работают. Возможно, мне просто не хватает ссылки или чего-то в этом роде, но похоже, что официальная документация не очень детальна по этому вопросу, и я не смог найти хорошее описание.
Насколько я могу судить, одновременно может работать только один поток, а активный поток переключается каждые 10 инструкций или около того?
Где есть хорошее объяснение или вы можете его предоставить? Также было бы очень хорошо знать об общих проблемах, с которыми вы сталкиваетесь при использовании потоков с Python.
Python - это довольно простой язык для внедрения, но с некоторыми оговорками. Самое важное, о чем вам нужно знать, - это Global Interpreter Lock. Это позволяет только одному потоку получить доступ к интерпретатору. Это означает две вещи: 1) вы редко когда-либо используете оператор блокировки в python и 2) если вы хотите воспользоваться преимуществами многопроцессорных систем, вы должны использовать отдельные процессы. РЕДАКТИРОВАТЬ: Я также должен указать, что вы можете поместить часть кода на C / C ++, если хотите также обойти GIL.
Таким образом, вам нужно пересмотреть, почему вы хотите использовать потоки. Если вы хотите распараллелить свое приложение, чтобы воспользоваться преимуществами двухъядерной архитектуры, вам необходимо подумать о том, чтобы разбить приложение на несколько процессов.
Если вы хотите улучшить отзывчивость, вам следует РАССМАТРИВАТЬ использование потоков. Однако есть и другие альтернативы, а именно микронить . Есть также несколько фреймворков, на которые стоит обратить внимание:
источник
Ниже приведен базовый образец резьбы. Будет создано 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
метод ...Конечно, это только самые основы. Со временем вы захотите узнать о семафорах, мьютексах и блокировках для синхронизации потоков и передачи сообщений.
источник
Используйте потоки в python, если отдельные воркеры выполняют операции ввода-вывода. Если вы пытаетесь масштабировать несколько ядер на машине, либо найдите хорошую структуру IPC для python, либо выберите другой язык.
источник
Примечание: где бы я ни упоминал,
thread
я имею в виду именно потоки в Python, пока явно не указано иное.В 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()
Если поток выполняет некоторые операции, связанные с вводом-выводом (доступ к файловой системе Ex или
сетевой ввод-вывод)
GIL освобождается всякий раз, когда поток ожидает завершения некоторой операции ввода-вывода.
Which thread to switch to next?
У интерпретатора нет собственного планировщика. Какой поток становится запланированным в конце интервала, решает операционная система. .
источник
Одним из простых решений GIL является модуль многопроцессорности . Его можно использовать как замену модулю потоковой передачи, но вместо потоков используется несколько процессов интерпретатора. Из-за этого накладных расходов немного больше, чем при простой потоковой передаче для простых вещей, но это дает вам преимущество реального распараллеливания, если оно вам нужно. Он также легко масштабируется на несколько физических машин.
Если вам нужно по-настоящему крупномасштабное распараллеливание, я бы посмотрел дальше, но если вы просто хотите масштабировать до всех ядер одного компьютера или нескольких разных без всей работы, которая могла бы пойти на реализацию более всеобъемлющей структуры, то это для вас .
источник
Помните, что GIL настроен на то, чтобы время от времени опрашиваться, чтобы показать появление нескольких задач. Этот параметр можно точно настроить, но я предлагаю предположить, что должна быть работа, которую выполняют потоки, иначе множество переключений контекста вызовут проблемы.
Я бы пошел так далеко, что предложил несколько родительских процессоров и попытался бы сохранить одинаковые рабочие места на одном ядре (ах).
источник