Как потоки работают в Python и каковы общие подводные камни, специфичные для Python-потоков?

StackOverflow https://stackoverflow.com/questions/31340

  •  09-06-2019
  •  | 
  •  

Вопрос

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

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

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

Это было полезно?

Решение

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

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

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

Если вы хотите использовать многоядерный, обработка pyprocessing определяет API на основе процесса для выполнения реального распараллеливания.Тот Самый БОДРОСТЬ ДУХА также включает в себя несколько интересных тестов.

Другие советы

Python - довольно простой язык для работы с потоками, но есть некоторые оговорки.Самое большое, о чем вам нужно знать, - это Глобальная блокировка интерпретатора.Это позволяет только одному потоку получить доступ к интерпретатору.Это означает две вещи:1) вы редко сталкиваетесь с использованием оператора lock в 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() на объекте thread, который автоматически вызовет run способ...

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

Используйте потоки в python, если отдельные работники выполняют операции ввода-вывода с привязкой.Если вы пытаетесь масштабировать несколько ядер на компьютере, либо найдите хороший МПК фреймворк для 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 переключение потоков обычно происходило после фиксированного значения no в инструкциях python.По умолчанию было установлено значение 100. Оказалось, что не очень хорошая политика для принятия решения о том, когда должно происходить переключение, поскольку время, затрачиваемое на выполнение одной инструкции, может очень сильно увеличиваться от миллисекунды до даже секунды.Поэтому освобождение GIL после каждого 100 инструкции, независимо от времени, которое требуется для их выполнения, - это плохая политика.

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

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

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

Which thread to switch to next?

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top