Теперь Кью работает в режиме чтения

Мы сохранили весь контент, но добавить что-то новое уже нельзя

Как в потоках python обойти GIL?

(создание процесса создаёт много накладных расходов и код НЕ ускоряется от параллельных вычислений, а даже замедляется)
ПрограммированиеPython+1
Максим Горохов
  ·   · 10,5 K
Представитель хостинг-провайдера Eternalhost...  · 3 февр 2023
Первый шаг к устранению GIL — понять, почему он существует. 
Зачем обычно нужны блокировки в многопоточной программе? Для предотвращения так называемого состояния гонки данных (race condition), когда два потока одновременно пытаются получить доступ к общей переменной, и для того, чтобы сделать определенные операции атомарными с точки зрения других потоков. 
Скажем, у вас есть последовательность операторов, которая изменяет некоторую структуру данных. Если вы не залочите окружение последовательности, то другой поток может получить доступ к структуре данных где-то в середине модификации и получить сломанное неполное представление.
Или, скажем, вы увеличиваете одну и ту же переменную из нескольких потоков. Если операция приращения не является атомарной и не защищена блокировкой, то конечное значение переменной может быть меньше общего количества приращений. Это типичная гонка данных:
  • Поток 1 считывает значение x.
  • Поток 2 считывает значение x.
  • Поток 1 записывает обратно значение x + 1.
  • Поток 2 записывает обратно значение x + 1, тем самым отбрасывая изменения, сделанные потоком 1.
В Python операция += не является атомарной, поскольку состоит из нескольких инструкций байт-кода. Чтобы увидеть, как это может привести к гонкам данных, установите интервал переключения на 0,000001 и запустите следующую функцию в несколько потоков:
sum = 0

def f():
    global sum
    for _ in range(1000):
        sum += 1
Зачем нужен GIL
  • Подсчет ссылок. Каждый объект Python имеет поле счетчика ссылок. В этом поле подсчитывается количество мест, которые ссылаются на объект: другие объекты Python, локальные и глобальные переменные C. Еще одно место увеличивает счетчик ссылок. На одно место меньше уменьшает его. Когда счетчик ссылок достигает нуля, объект освобождается. Если бы не GIL, некоторые декременты могли перезаписывать друг друга, и объект оставался бы в памяти навсегда. Что еще хуже, перезаписанные приращения могут привести к освобождению объекта с активными ссылками.
  • Изменяемые структуры данных. Списки, словари и наборы не используют внутреннюю блокировку, но благодаря GIL их можно безопасно использовать в многопоточных программах. 
  • Глобальные данные и данные интерпретатора. Точно так же GIL позволяет потокам безопасно получать доступ к глобальным данным и данным интерпретатора: загруженным модулям, предварительно выделенным объектам, интернированным строкам и т. д.
  • Расширения С. Разработчики могут предположить, что только один поток выполняет их расширение C в любой момент времени. Таким образом, им не нужно использовать дополнительную блокировку, чтобы сделать код потокобезопасным. Когда они хотят запускать код параллельно, они могут выпустить GIL.
Можно ли обойтись без GIL?
Чтобы удалить GIL и по-прежнему иметь работающий интерпретатор, вам нужно найти альтернативные механизмы для обеспечения безопасности потоков. 
Наиболее заметной попыткой реализации подобного был проект Gilectomy Ларри Хастингса, начатый в 2016 году. Гастингс разветвил CPython, удалил GIL, изменил подсчет ссылок, чтобы использовать атомарные приращения и декременты, и установил множество мелких блокировок для защиты изменяемых структур данных и всего интерпретатора. данные.
Gilectomy может запускать некоторый код Python и запускать его параллельно. Однако однопоточная производительность CPython была скомпрометирована. Одни только атомарные приращения и декременты добавляли около 30% накладных расходов. Гастингс попытался решить эту проблему, реализовав буферизованный подсчет ссылок. 
В итоге стало очевидно, что Gilectomy не собирается сливаться с CPython. Гастингс прекратил работу над проектом. Однако это не было полным провалом. Gilectomy показал, почему удалить GIL из CPython практически невозможно:
  1. Сборка мусора на основе подсчета ссылок не подходит для многопоточности. Единственное решение — реализовать отслеживающий сборщик мусора, который реализуют JVM, CLR, Go и другие среды выполнения без GIL.
  2. Удаление GIL нарушает существующие расширения C. Нет никакого способа обойти это.
Просто о настройках и администрировании сайта в нашем блоге.Перейти на eternalhost.net/blog
Анонимный комментарий
9 февр 2023
запуск функции в 4 потоках с таким интервалом(10^-6 сек) к гонке данных не приводят :)
Разработчик на: C#, C++, Python  · 3 февр 2023
Могу посоветовать следующее: 1. Использование другой реализации Python, такой как Jython или IronPython, которые не имеют GIL. 2. Использование многопроцессорных библиотек, таких как multiprocessing, которые позволяют... Читать далее
Платная консультация, создание скриптов и программного обеспечения на заказ.Перейти на t.me/EN3RGY_TG
Анонимный комментарий
9 февр 2023
IronPython и Jython не пойдут… Jython - вообще python 2(древняя версия), а у IronPython проблемы с кроссплатформено... Читать дальше
Специалист в области управления и информатики в...  · 2 февр 2023
1. Использовать рабочие процессы многократно. 2. Разделить программу на две части: менеджер задач (сервер) и рабочие (которые выполняют задачи). По сути использовать распределенную архитектуру. В итоге: 1) Вы получите... Читать далее
Openstack DevOps and IBM/Informix Certified DBA...  · 2 февр 2023
Многопроцессорная обработка VS многопоточность.  Самый популярный способ - попробовать использовать многопроцессорный подход, Вы используете несколько процессов вместо потоков.  Каждый процесс Python получает свой собственный... Читать далее
Анонимный комментарий
9 февр 2023
Проблема как раз в том, что процесс "весит" ~10МБ БЕЗ данных :( Т.е. каждый такой "поток" весит всегда >10... Читать дальше
Инженер путей сообщения – строитель  · 1 февр 2023
Не надо ничего обходить. Просто надо оптимизировать работу потоков. А оптимизируется она за счёт снижения количества синхронизированного кода. Это достигается за счёт того, что сначала создаются независимые массивы данных... Читать далее
А в си, что, нет динамической памяти(есть ведь функции для работы с памятью из "кучи": malloc, calloc, realloc... Читать дальше