Поиск…


замечания

Почему существует GIL?

GIL работает в CPython с момента создания потоков Python в 1992 году. Он разработан для обеспечения безопасности потоков при работе с кодом Python. Устные переводчики Python, написанные с помощью GIL, предотвращают одновременное выполнение нескольких собственных потоков от байт-кодов Python. Это облегчает для плагинов обеспечение того, чтобы их код был потокобезопасным: просто заблокируйте GIL, и только ваш активный поток может работать, поэтому ваш код автоматически потокобезопасен.

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

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

Обратите внимание, что GIL не является требованием языка Python. Следовательно, вы не можете получить доступ к GIL напрямую из стандартного кода python. Не все реализации Python используют GIL.

Интерпретаторы, которые имеют GIL: CPython, PyPy, Cython (но вы можете отключить GIL с nogil )

Переводчики, не имеющие GIL: Jython, IronPython

Подробная информация о том, как работает GIL:

Когда поток работает, он блокирует GIL. Когда поток хочет запустить, он запрашивает GIL и ждет, пока он не будет доступен. В CPython, перед версией 3.2, текущий поток проверял бы после некоторого количества инструкций python, чтобы узнать, нужен ли другой код блокировке (то есть, он освободил блокировку, а затем запросил ее снова). Этот метод, как правило, вызывал головокружение потоков, главным образом потому, что поток, который освободил блокировку, приобрел его снова, прежде чем ожидающие потоки имели возможность проснуться. Начиная с 3.2, потоки, которые хотят, чтобы GIL ожидал блокировки в течение некоторого времени, и после этого времени, они устанавливают общую переменную, которая заставляет текущую нить давать. Однако это может привести к значительному увеличению времени выполнения. См. Ссылки ниже от dabeaz.com (в разделе ссылок) для более подробной информации.

CPython автоматически освобождает GIL, когда поток выполняет операцию ввода-вывода. Библиотеки обработки изображений и операции хруста с номером номера освобождают GIL перед выполнением их обработки.

Преимущества GIL

Для переводчиков, которые используют GIL, GIL является системным. Он используется для сохранения состояния приложения. Преимущества включают:
  • Сбор мусора - подсчет ссылок на потоки должен быть изменен, когда GIL заблокирован. В CPython вся коллекция garbarge привязана к GIL. Это большой; см. статью wiki python.org о GIL (см. ниже в ссылках) для получения подробной информации о том, что должно быть функционально, если вы хотите удалить GIL.
  • Простота для программистов, занимающихся GIL - блокировка, все упрощена, но легко кодируется
  • Упрощает импорт модулей с других языков

Последствия GIL

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

Рекомендации:

https://wiki.python.org/moin/GlobalInterpreterLock - краткое изложение того, что он делает, мелкие детали всех преимуществ

http://programmers.stackexchange.com/questions/186889/why-was-python-written-with-the-gil - четко написанное резюме

http://www.dabeaz.com/python/UnderstandingGIL.pdf - как работает GIL и почему он замедляется на нескольких ядрах

http://www.dabeaz.com/GIL/gilvis/index.html - визуализация данных, показывающих, как GIL блокирует потоки

http://jeffknupp.com/blog/2012/03/31/pythons-hardest-problem/ - простая для понимания история проблемы GIL

https://jeffknupp.com/blog/2013/06/30/pythons-hardest-problem-revisited/ - подробная информация о способах работы с ограничениями GIL

Multiprocessing.Pool

Простой ответ, когда вы спрашиваете, как использовать потоки в Python, это: «Не используйте. Вместо этого используйте процессы». Многопроцессорный модуль позволяет создавать процессы с похожим синтаксисом для создания потоков, но я предпочитаю использовать их удобный объект Pool.

Используя код, который Дэвид Бэйзли впервые использовал для выявления опасностей потоков против GIL , мы перепишем его с помощью многопроцессорной обработки. Пул :

Код Дэвида Бэзли, который показал проблемы с резьбой GIL

from threading import Thread
import time
def countdown(n):
    while n > 0:
        n -= 1

COUNT = 10000000

t1 = Thread(target=countdown,args=(COUNT/2,))
t2 = Thread(target=countdown,args=(COUNT/2,))
start = time.time()
t1.start();t2.start()
t1.join();t2.join()
end = time.time()
print end-start
Переписано с использованием многопроцессорности.Pool:
import multiprocessing
import time
def countdown(n):
    while n > 0:
        n -= 1

COUNT = 10000000

start = time.time()
with multiprocessing.Pool as pool:
    pool.map(countdown, [COUNT/2, COUNT/2])

    pool.close()
    pool.join()

end = time.time()
print(end-start)

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

Я обнаружил, что даже используя оператор with , если вы не закрываете и не присоединяетесь к пулу, процессы продолжают существовать. Чтобы очистить ресурсы, я всегда закрываю и присоединяюсь к моим пулам.

Cython nogil:

Cython - альтернативный интерпретатор python. Он использует GIL, но позволяет отключить его. См. Их документацию

В качестве примера, используя код, который Дэвид Бэйсли впервые использовал, чтобы показать опасности потоков против GIL , мы перепишем его с помощью nogil:

Код Дэвида Бэзли, который показал проблемы с резьбой GIL

from threading import Thread
import time
def countdown(n):
    while n > 0:
        n -= 1

COUNT = 10000000

t1 = Thread(target=countdown,args=(COUNT/2,))
t2 = Thread(target=countdown,args=(COUNT/2,))
start = time.time()
t1.start();t2.start()
t1.join();t2.join()
end = time.time()
print end-start

Переписано с использованием nogil (ТОЛЬКО РАБОТАЕТ В ЦИТОНЕ):

from threading import Thread
import time
def countdown(n):
    while n > 0:
        n -= 1

COUNT = 10000000

with nogil:
    t1 = Thread(target=countdown,args=(COUNT/2,))
    t2 = Thread(target=countdown,args=(COUNT/2,))
    start = time.time()
    t1.start();t2.start()
    t1.join();t2.join()
    
end = time.time()
print end-start

Это так просто, если вы используете cython. Обратите внимание, что в документации указано, что вы не должны изменять какие-либо объекты python:

Код в теле оператора не должен каким-либо образом манипулировать объектами Python и не должен называть ничего, что манипулирует объектами Python, без предварительного повторного приобретения GIL. Cython в настоящее время не проверяет это.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow