Все потоки Python выполняются на одном ядре - PullRequest
52 голосов
/ 21 декабря 2010

У меня есть программа на Python, которая порождает много потоков, запускает 4 за раз, и каждый выполняет дорогостоящую операцию. Псевдокод:

for object in list:
    t = Thread(target=process, args=(object))
    # if fewer than 4 threads are currently running, t.start(). Otherwise, add t to queue

Но когда программа запускается, Activity Monitor в OS X показывает, что 1 из 4 логических ядер на 100%, а остальные почти на 0. Очевидно, я не могу заставить ОС делать что-либо, но я никогда не приходилось обращать внимание на производительность в многопоточном коде, как это раньше, поэтому мне было интересно, что я что-то упускаю или неправильно что-то понимаю.

Спасибо.

Ответы [ 3 ]

65 голосов
/ 21 декабря 2010

Обратите внимание, что во многих случаях (и практически во всех случаях, когда ваша "дорогая операция" является вычислением, реализованным в Python), несколько потоков не будут работать одновременно из-за Global Interpreter Lock (GIL) Python.

GIL - это блокировка уровня интерпретатора.Эта блокировка предотвращает выполнение нескольких потоков одновременно в интерпретаторе Python.Каждый поток, который хочет работать, должен ждать освобождения GIL другим потоком, что означает, что ваше многопоточное приложение Python по сути однопоточное, верно?Да.Не совсем.Вроде.

CPython использует так называемые потоки «операционной системы» под оболочками, то есть каждый раз, когда делается запрос на создание нового потока, интерпретатор фактически вызывает библиотеки операционной системы и ядро ​​длясоздать новую тему.Например, это то же самое, что и Java.Таким образом, в памяти у вас действительно есть несколько потоков, и обычно операционная система контролирует, какой поток планируется запустить.На многопроцессорной машине это означает, что у вас может быть много потоков, распределенных по нескольким процессорам, и все они с радостью пойдут на работу.

Однако, хотя CPython использует потоки операционной системы (теоретически, позволяя нескольким потокам выполняться внутриинтерпретатор одновременно), интерпретатор также вынуждает GIL быть полученным потоком, прежде чем он сможет получить доступ к интерпретатору и стеку и может изменять объекты Python в памяти все в произвольном порядке.Последний момент заключается в том, почему существует GIL: GIL предотвращает одновременный доступ к объектам Python несколькими потоками.Но это не спасает вас (как показано в примере с Банком) от существа, чувствительного к блокировке;Вы не получите бесплатную поездку.GIL предназначен для защиты памяти переводчиков, а не вашего здравомыслия.

См. Раздел «Глобальная блокировка интерпретатора» Сообщение Джесси Ноллера для получения более подробной информации.

КомуОбойти эту проблему, посмотрите Модуль многопроцессорной обработки Python .

Несколько процессов (с разумным использованием IPC) [...] являются гораздо лучшим подходом к написанию приложений для мульти-CPU коробки, чем темы.

- Гвидо ван Россум (создатель Python)

9 голосов
/ 21 декабря 2010

Python имеет глобальную блокировку интерпретатора, которая может предотвратить одновременную обработку потоков интерпретируемого кода.

http://en.wikipedia.org/wiki/Global_Interpreter_Lock

http://wiki.python.org/moin/GlobalInterpreterLock

Чтобы обойти это, попробуйте многопроцессорный модуль , как указано здесь:

Избегает ли запуск отдельных процессов Python GIL?

2 голосов
/ 21 декабря 2010

AFAIK, в CPython глобальная блокировка интерпретатора означает, что не может быть одновременно запущено более одного блока кода Python. Хотя это ни на что не влияет ни на одном процессоре / одноядерном компьютере, но на многоядерном компьютере это означает, что у вас фактически работает только один поток за один раз, в результате чего все остальные ядра простаивают.

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