threading — Потоковый параллелизм

Исходный код: Lib/threading.py


Модуль создает интерфейсы многопоточности более высокого уровня поверх модуля _thread более низкого уровня. См. также модуль queue.

Изменено в версии 3.7: Модуль мог быть необязательным, теперь он доступен всегда.

Примечание

Хотя они не перечислены ниже, имена camelCase, используемый для некоторых методов и функций в этом модуле серии Python 2.x, по-прежнему поддерживаются этим модулем.

Модуль определяет следующие функции:

threading.active_count()

Возвращает количество Thread объектов, находящихся в настоящее время в живых. Число возвращенный равно длине списка, возвращенный по enumerate().

threading.current_thread()

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

threading.excepthook(args, /)

Обработать неподъемное исключение, возбужденное Thread.run().

Аргумент args имеет следующие атрибуты:

  • exc_type: тип исключения.
  • exc_value: исключение значение, может быть None.
  • exc_traceback: исключение трейсбэк, может быть None.
  • thread: поток, который вызвал исключение, может быть None.

Если exc_type SystemExit, исключение игнорируется. В противном случае исключение распечатывается на sys.stderr.

Если эта функция вызывает исключение, sys.excepthook() вызывается для его обработки.

threading.excepthook() можно переопределить для управления обработкой неподтвержденных исключений, создаваемых Thread.run().

Хранение exc_value с помощью пользовательского хук может создать ссылочный цикл. Он должен быть очищен явным образом, чтобы прервать ссылочный цикл, если исключение больше не требуется.

Сохранение thread с помощью пользовательского хук может восстановить его, если для него задан объект, который завершается. Избегайте сохранения thread после завершения пользовательского хук, чтобы избежать восстановления объектов.

См.также

sys.excepthook() обрабатывает неподъемные исключения.

Добавлено в версии 3.8.

threading.get_ident()

Возвращает идентификатор «поток» текущего поток. Это ненулевое целое число. Его значение не имеет прямого значения; он предназначен для используемый в качестве магического cookie, например, для индексации словаря потокоспецифичных данных. Идентификаторы потоков могут быть повторно использованы при выходе из поток и создании другого поток.

Добавлено в версии 3.3.

threading.get_native_id()

Возвращает собственный интегральный идентификатор потока текущего поток, назначенного ядром. Это неотрицательное целое число. Его значение может быть используемый для однозначной идентификации этого конкретного поток в масштабах всей системы (до тех пор, пока поток не прекратится, после чего значение может быть повторно использован ОС).

Availability: Windows, FreeBSD, Linux, macOS, OpenBSD, NetBSD, AIX.

Добавлено в версии 3.8.

threading.enumerate()

Возвращает список всех объектов Thread находящихся в данный момент в живых. Список включает демонические потоки, фиктивные поток объекты, созданные current_thread(), и основные поток. Он исключает завершенные потоки и потоки, которые еще не были запущены.

threading.main_thread()

Возвращает главный объект Thread. В нормальных условиях основным поток является поток, с которого был начат Python интерпретатор.

Добавлено в версии 3.4.

threading.settrace(func)

Функция следа установить для всего потоки началась с модуля threading. Перед вызовом метода func sys.settrace() передается в поток для каждого run().

threading.setprofile(func)

Функция профиля установить для всех потоки, запущенных из модуля threading. Перед вызовом метода func sys.setprofile() передается в поток для каждого run().

threading.stack_size([size])

Возвращает размер стека поток используемый при создании новых потоки. Необязательный аргумент size указывает размер стека, который должен быть используемый для созданного впоследствии потоки, и должен быть равен 0 (использовать платформу или настроенное значение по умолчанию) или положительному целому числу значение не менее 32 768 (32 KiB). Если size не указан, 0 будет используемый. Если изменение размера стека поток не поддерживается, поднимается RuntimeError. Если указанный размер стека недопустим, ValueError увеличивается, а размер стека не изменяется. 32 KiB в настоящее время является минимальным поддерживаемым размером стека значение, чтобы гарантировать достаточное пространство для самого интерпретатор. Следует отметить, что некоторые платформы могут иметь особые ограничения на значения размера стека, такие как требование минимального размера стека > 32 KiB или требование выделения кратного размера страницы системной памяти - для получения дополнительной информации следует обратиться к документации платформы (4 KiB страниц являются общими; использование кратных 4096 для размера стека является предложенным подходом при отсутствии более конкретной информации).

Availability: Windows, systems with POSIX threads.

Модуль также определяет следующую константу:

threading.TIMEOUT_MAX

Максимально допустимая значение для параметра timeout блокирующих функций (Lock.acquire(), RLock.acquire(), Condition.wait() и т. д.). При указании времени ожидания, превышающего это значение, возникает OverflowError.

Добавлено в версии 3.2.

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

Конструкция этого модуля свободно основана на модели многопоточности Java. Однако если Java делает блокировки и переменные условий основным поведением каждого объекта, они являются отдельными объектами в Python. Python класс Thread поддерживает подмножество поведения класса Java Thread; в настоящее время нет приоритетов, нет потоков групп, и потоки не могут быть уничтожены, остановлены, приостановлены, возобновлены или прерваны. Статические методы класса Java Thread при реализации сопоставляются с функциями уровня модуля.

Все способы, описанные ниже, выполняются атомарно.

Локальные данные потока

Thread-локальная данные - это данные, значения которых поток специфичны. Чтобы управлять поток-локальная данными, просто создайте сущность local (или подкласс) и сохраните атрибуты на нем:

mydata = threading.local()
mydata.x = 1

значения сущность будет отличаться для отдельных потоки.

class threading.local

Класс, представляющий поток-локальная данные.

Дополнительные сведения и подробные примеры см. в строка документации модуля _threading_local.

Объекты Thread

Класс Thread представляет действие, выполняемое в отдельном поток управления. Существует два способа задания действия: передача вызываемого объекта конструктору или переопределение метода run() в подкласс. Никакие другие методы (кроме конструктора) не должны переопределяться в подкласс. Другими словами, только переопределяют методы __init__() и run() этого класса.

После создания объекта поток его действие должно быть запущено путем вызова метода start() поток. Это вызывает метод run() в отдельном поток управления.

После запуска активности поток поток считается «живым». Он перестает быть живым, когда его метод run() заканчивается - либо обычно, либо возбуждая необработанное исключение. Метод is_alive() проверяет, жив ли поток.

Другие потоки могут вызывать метод join() поток. Это блокирует поток вызова до тех пор, пока не завершится поток, метод join() которого вызывается.

У поток есть имя. Имя может быть передано конструктору и прочитано или изменено через name атрибут.

Если метод run() вызывает исключение, threading.excepthook() вызывается для его обработки. По умолчанию threading.excepthook() игнорируется молча SystemExit.

Поток можно пометить как поток «демон». Значение этого флага состоит в том, что вся программа Python выходит, когда остаются только потоки демона. Начальный значение наследуется от создающего поток. Флаг можно установить с помощью свойства daemon или аргумента конструктора daemon.

Примечание

Демоны потоки резко останавливаются при остановке. Их ресурсы (например, открытые файлы, транзакции базы данных и т.д.) могут быть разблокированы неправильно. Если вы хотите, чтобы ваши потоки прекратились изящно, сделайте их недемоническими и используйте подходящий механизм сигнализации, такой как Event.

Существует объект «основной поток»; это соответствует начальному поток управления в программе Python. Это не демон поток.

Существует возможность создания «фиктивных поток объектов». Это поток объекты, соответствующие «чужим потоки,», которые являются потоки управления, запущенными вне модуля многопоточности, например, непосредственно из C код. Фиктивные поток объекты имеют ограниченную функциональность; они всегда считаются живыми и демоническими, и не могут быть join(). Они никогда не удаляются, так как невозможно обнаружить прекращение инопланетного потоки.

class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)

Этот конструктор всегда должен вызываться с ключевой аргументами. Аргументы are:

group следует None; зарезервирован для будущего расширения при реализации класса ThreadGroup.

target - вызываемый объект, вызываемый методом run(). Значение по умолчанию - None, т.е. ничего не называется.

name - имя поток. По умолчанию уникальное имя создается из формы «Thread-N», где N - малое десятичное число.

args - это кортеж аргументов для целевого вызова. По умолчанию - ().

kwargs - словарь аргументов ключевой для целевого вызова. По умолчанию - {}.

Если не None, daemon явно устанавливает, является ли поток демоническим. При None (по умолчанию) свойство daemonic наследуется от текущего поток.

Если подкласс переопределяет конструктор, необходимо перед выполнением других действий с поток обязательно вызвать конструктор базового класса (Thread.__init__()).

Изменено в версии 3.3: Добавлен аргумент daemon.

start()

Запустить операцию потока.

Он должен вызываться не более одного раза на поток объект. Он обеспечивает вызов метода run() объекта в отдельном поток управления.

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

run()

Способ, представляющий активность поток.

Этот метод можно переопределить в подкласс. Стандартный метод run() вызывает вызываемый объект, переданный конструктору объекта в качестве аргумента target, если таковой имеется, с позиционными и ключевой аргументами, взятыми из аргументов args и kwargs соответственно.

join(timeout=None)

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

Когда аргумент timeout присутствует, а не None, он должен быть числом с плавающей запятой, указывающим тайм-аут для операции в секундах (или ее долях). Как join() всегда возвращает None, вы должны назвать is_alive() после join(), чтобы решить, произошел ли перерыв - если поток все еще жив, требование join(), рассчитанное.

Если аргумент timeout отсутствует или None, операция будет блокироваться до завершения поток.

Поток может быть join() многократно.

join() вызывает RuntimeError при попытке присоединения к текущему поток, поскольку это может привести к взаимоблокировке. Также ошибка при join() поток перед его запуском и попытках создать такое же исключение.

name

Строка используемый только для целей идентификации. У него нет семантики. Нескольким потоки может быть присвоено одинаковое имя. Исходное имя задается конструктором.

getName()
setName()

Старый API получателя/установщика для name; используйте его непосредственно в качестве свойства.

ident

Идентификатор поток этого поток или None, если поток не был запущен. Это ненулевое целое число. См. функцию get_ident(). Идентификаторы потоков могут быть повторно использованы при выходе из поток и создании другого поток. Идентификатор доступен даже после выхода из поток.

native_id

Собственный интегральный идентификатор потока этого потока. Это неотрицательное целое число или None, если поток не был запущен. См. функцию get_native_id(). Представляет идентификатор потока (TID), назначенный поток операционной системой (ядром). Его значение может быть используемый для однозначной идентификации этого конкретного поток в масштабах всей системы (до тех пор, пока поток не прекратится, после чего значение может быть повторно использован ОС).

Примечание

Аналогично идентификаторам процессов, идентификаторы потоков действительны (гарантированно уникальные для всей системы) только с момента создания поток до завершения поток.

Availability: Требуется get_native_id() функция.

Добавлено в версии 3.8.

is_alive()

Возвращает, жив ли поток.

Этот метод возвращает True непосредственно перед запуском метода run() до завершения метода run(). Функция модуля enumerate() возвращает список всех активных потоки.

daemon

Логическое значение, указывающее, является ли этот поток демоном потоком (True) или нет (False). Это должно быть установлено перед вызовом start(), в противном случае RuntimeError поднимается. Его первоначальный значение наследуется от создающего поток; главный поток не является демоном поток и поэтому все потоки созданы в главном поток по умолчанию daemon = False.

Вся программа Python завершается, когда не осталось ни одного живого потоки, не являющегося демоном.

isDaemon()
setDaemon()

Старый API получателя/установщика для daemon; используйте его непосредственно в качестве свойства.

Детали реализации CPython: В CPython, из-за Глобальной блокировки интерпретатора (GIL), только один поток может выполнять Python код одновременно (даже несмотря на то, что некоторые библиотеки, ориентированные на производительность, могут преодолеть это ограничение). Если вы хотите, чтобы ваше приложение лучше использовало вычислительные ресурсы многоядерных машин, вам рекомендуется использовать multiprocessing или concurrent.futures.ProcessPoolExecutor. Однако многопоточность по-прежнему является подходящей моделью для одновременного выполнения нескольких I/O-связанный задач.

Объекты Lock

Примитивная блокировка - это примитив синхронизации, который не принадлежит определенному поток при блокировке. В Python, в настоящее время это самый низкий доступный примитив синхронизации, реализованный непосредственно модулем расширения _thread.

Примитивная блокировка находится в одном из двух состояний, «заблокирована» или «разблокирована». Он создается в разблокированном состояние. Он имеет два основных метода: acquire() и release(). Когда состояние разблокирован, acquire() изменяет состояние на заблокированный и немедленно возвращает. Когда состояние заблокирован, acquire() блокирует до тех пор, пока вызов на release() в другом поток не изменит его на разблокированный, затем acquire() вызов сбрасывает его на заблокированный и возвращает. Метод release() должен вызываться только в заблокированном состояние; он изменяет состояние на разблокированный и немедленно возвращает. При попытке разблокировать блокировку RuntimeError поднимается.

Замки также поддерживают протокол управления контекстом.

Когда в acquire() заблокировано несколько поток, ожидающих, когда состояние перейдет в режим разблокирования, только один поток продолжается, когда release() вызов сбрасывает состояние в режим разблокирования; какой из потоки ожидания не определен и может изменяться в различных реализациях.

Все методы выполняются атомарно.

class threading.Lock

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

Обратите внимание, что Lock фактически является заводской функцией, которая возвращает сущность наиболее эффективной версии бетонного класса Lock, поддерживаемой платформой.

acquire(blocking=True, timeout=-1)

Получение блокировки, блокировки или неблокировки.

Если для аргумента blocking задано значение True (по умолчанию), блокировать блокировку до тех пор, пока блокировка не будет разблокирована, а затем установите для него значение locked и возвращает True.

При вызове с аргументом blocking, имеющим значение False, не блокировать. Если вызов с blocking set to True блокируется, возвращает False немедленно; в противном случае установите блокировку на заблокированный и возвращает True.

При вызове с аргументом timeout с плавающей запятой, установленным в положительное значение, блокировать не более чем на число секунд, заданное timeout, и до тех пор, пока блокировка не может быть получена. Аргумент timeout -1 указывает неограниченное ожидание. Запрещается указывать timeout, если blocking имеет значение false.

Возвращает значение - True, если замок приобретен успешно, False, если не (например, если timeout истек).

Изменено в версии 3.2: Параметр timeout является новым.

Изменено в версии 3.2: Захват блокировки теперь может прерываться сигналами на POSIX, если его поддерживает базовая реализация многопоточности.

release()

Отпустить замок. Это можно вызвать из любого поток, а не только поток, который приобрел блокировку.

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

При вызове разблокированной блокировки поднимается RuntimeError.

Нет возвращаемого значения.

locked()

Возвращает true, если блокировка получена.

Объекты RLock

Входящая блокировка - это примитив синхронизации, который может быть получен несколько раз одним и тем же поток. Внутренне он использует понятия «владеющий поток» и «уровень рекурсии» в дополнение к заблокированным/разблокированным состояние, используемый примитивными блокировками. В запертом состояние замок принадлежит некоторым поток; в разблокированном состояние ни один поток не владеет им.

Чтобы заблокировать блокировку, поток вызывает свой метод acquire(); это возвращает, как только поток владеет блокировкой. Чтобы разблокировать блокировку, поток вызывает свой метод release(). acquire()/release() пары вызовов могут быть вложенными; только конечный release() (release() самой внешней пары) сбрасывает блокировку на разблокированную и позволяет продолжить другой поток, заблокированный в acquire().

Фиксаторы также поддерживают протокол управления контекстом.

class threading.RLock

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

Обратите внимание, что RLock фактически является заводской функцией, которая возвращает сущность наиболее эффективной версии бетонного класса RLock, поддерживаемой платформой.

acquire(blocking=True, timeout=-1)

Установка замка, блокировки или неблокировки.

При вызове без аргументов: если этому поток уже принадлежит блокировка, увеличьте уровень рекурсии на единицу и немедленно возвращает. В противном случае, если блокировкой владеет другой поток, блокировать блокировку до тех пор, пока она не будет разблокирована. Как только блокировка разблокирована (не принадлежит никакому поток), захватите владельца, установите уровень рекурсии равным единице и возвращает. Если несколько поток заблокированы, ожидая разблокирования блокировки, только один за раз сможет захватить право собственности на блокировку. В данном случае нет возвращает значение.

При вызове с аргументом blocking, имеющим значение true, выполните те же действия, что и при вызове без аргументов, и возвращает True.

При вызове с аргументом blocking, имеющим значение false, не блокировать. Если вызов без аргумента блокируется, возвращает False немедленно; в противном случае делайте то же самое, что при вызове без аргументов, и возвращает True.

При вызове с аргументом timeout с плавающей запятой, установленным в положительное значение, блокировать не более чем на число секунд, заданное timeout, и до тех пор, пока блокировка не может быть получена. Возвращает True если блокировка была получена, значение false, если истекло время ожидания.

Изменено в версии 3.2: Параметр timeout является новым.

release()

Освободить блокировку, уменьшив уровень рекурсии. Если после уменьшения значение равно нулю, сбросьте блокировку на разблокированную (не принадлежащую никакому поток), и если другие потоки заблокированы в ожидании разблокировки блокировки, разрешите продолжить работу только одному из них. Если после уменьшения уровень рекурсии остается ненулевым, блокировка остается заблокированной и принадлежащей вызывающему поток.

Вызывать этот метод можно только в том случае, если блокировка принадлежит вызывающему поток. Если этот метод вызывается при разблокировании блокировки, возникает RuntimeError.

Нет возвращаемого значения.

Объекты Condition

Переменная условия всегда связана с какой-либо блокировкой; он может быть передан или один будет создан по умолчанию. Передача одной переменной в удобна, если несколько переменных условий должны иметь одну и ту же блокировку. Блокировка является частью объекта условия: отследить ее отдельно не требуется.

Переменная условия подчиняется протокол управления контекстом: с помощью with инструкция получает связанный замок на время действия заключенного блока. Методы acquire() и release() также вызывают соответствующие методы соответствующей блокировки.

Другие методы должны вызываться с удержанием связанной блокировки. Метод wait() снимает блокировку, а затем блокирует до тех пор, пока другой поток не пробуждает ее, вызывая notify() или notify_all(). Пробудившись, wait() вновь устанавливает блокировку и возвращает. Также можно указать тайм-аут.

Метод notify() запускает один из потоки, ожидающих переменную условия, если таковые имеются. Метод notify_all() активизирует все потоки, ожидающие переменную условия.

Примечание: методы notify() и notify_all() не снимают блокировку; это означает, что поток или пробужденный потоки не будут возвращает от их требования wait() немедленно, но только когда поток, который названный notify() или notify_all() наконец оставляет собственность замка.

Типичный стиль программирования с использованием переменных условий использует блокировку для синхронизации доступа к некоторым общим состояние; потоки, которые заинтересованы в конкретном изменении состояние вызова, wait() неоднократно до тех пор, пока они не увидят желаемый состояние, в то время как потоки, которые изменяют состояние вызов notify() или notify_all(), когда они меняют состояние таким образом, что это может быть желательным состояние для одного из официантов. Например, следующий код является общей ситуацией между производителем и потребителем с неограниченной буферной емкостью:

# Поглотить один элемент
with cv:
    while not an_item_is_available():
        cv.wait()
    get_an_available_item()

# Изготовить один элемент
with cv:
    make_an_item_available()
    cv.notify()

Проверка while цикла для состояния приложения необходима, так как wait() может возвращает после произвольного длительного времени, и условие, вызвавшее notify() вызов, может больше не иметь значения true. Это присуще многопоточному программированию. Метод wait_for() может быть используемый для автоматизации проверки состояния и упрощает вычисление тайм-аутов:

# Поглотить элемент
with cv:
    cv.wait_for(an_item_is_available)
    get_an_available_item()

Чтобы выбрать между notify() и notify_all(), подумайте, может ли одно изменение состояние быть интересным только для одного или нескольких потоки ожидания. Например, в типичной ситуации между производителем и потребителем добавление одного элемента в буфер требует только пробуждения одного потребительского поток.

class threading.Condition(lock=None)

Класс реализует объекты переменных условий. Переменная условия позволяет одному или нескольким потоки ждать уведомления от другого поток.

Если задан аргумент lock, а не None, он должен быть объектом Lock или RLock и используемый в качестве основной блокировки. В противном случае создается новый объект RLock, который используемый в качестве основной блокировки.

Изменено в версии 3.3: Изменен с функции фабрики на класс.

acquire(*args)

Получить базовую блокировку. Этот метод вызывает соответствующий метод для основной блокировки; возвращает значение независимо от того, что возвращает этот метод.

release()

Отпустить основную блокировку. Этот метод вызывает соответствующий метод для основной блокировки; нет возвращает значение.

wait(timeout=None)

Дождаться получения уведомления или истечения времени ожидания. Если вызывающий поток не получил блокировку при вызове этого метода, возникает RuntimeError.

Этот метод освобождает базовую блокировку, а затем блокирует ее до тех пор, пока она не будет пробуждена вызовом notify() или notify_all() для той же переменной условия в другом поток или пока не наступит дополнительное время ожидания. После пробуждения или тайм-аута он повторно приобретает замок и возвращает.

Когда аргумент timeout присутствует, а не None, он должен быть числом с плавающей запятой, указывающим тайм-аут для операции в секундах (или её долях).

Когда основной блокировкой является RLock, она не освобождается с помощью метода release(), так как это может фактически не разблокировать блокировку, когда она была получена несколько раз рекурсивно. Вместо этого используемый внутренний интерфейс класса RLock, который действительно разблокирует его, даже если он рекурсивно приобретался несколько раз. После этого используемый другой внутренний интерфейс для восстановления уровня рекурсии при повторном вызове блокировки.

Возвращает значение - True, если данный timeout не истек, в этом случае это - False.

Изменено в версии 3.2: Ранее метод всегда возвращал None.

wait_for(predicate, timeout=None)

Подождать, пока условие не получит значение true. predicate должен быть вызываемым, результат которого будет интерпретироваться как логический значение. Может быть предоставлена timeout, дающая максимальное время ожидания.

Этот служебный метод может вызывать wait() повторно до тех пор, пока не будет выполнен предикат или пока не наступит тайм-аут. Значение возвращает значение является последним значением возвращает значение предиката и вычисляется, чтобы False, истекло ли время ожидания метода.

Игнорирование функции тайм-аута, вызов этого метода примерно эквивалентен записи:

while not predicate():
    cv.wait()

Поэтому применяются те же правила, что и для wait(): блокировка должна удерживаться при вызове и повторно приобретается на возвращает. Предикат оценивается при удержании блокировки.

Добавлено в версии 3.2.

notify(n=1)

По умолчанию разбудить однин поток, ожидающего этого условия, если оно есть. Если вызывающий поток не получил блокировку при вызове этого метода, возникает RuntimeError.

Этот метод пробуждается не более n потоки, ожидающих переменную условия; это no-op, если ни один потоки не ожидает.

Текущая реализация просыпается ровно n потоки, если ждут хотя бы n потоки. Однако полагаться на такое поведение небезопасно. Будущая оптимизированная реализация может иногда пробуждаться более чем на n потоки.

Примечание: пробужденный поток фактически не возвращает от своего wait() вызова до тех пор, пока не сможет повторно захватить замок. Поскольку notify() не снимает блокировку, его вызывающий абонент должен.

notify_all()

Проснуться всем потокам ожидая этого условия. Этот метод действует как notify(), но будит всех ожидающих потоки вместо одного. Если вызывающий поток не получил блокировку при вызове этого метода, возникает RuntimeError.

Объекты Semaphore

Это один из древнейших примитивов синхронизации в истории информатики, изобретённый ранним голландским компьютерщиком эдсгером в. Дайкстрой (он используемый названия P() и V() вместо acquire() и release()).

Семафор управляет внутренним счетчиком, который уменьшается каждым acquire() вызовом и увеличивается каждым release() вызовом. Счетчик никогда не может быть ниже нуля; когда acquire() обнаруживает, что он равен нулю, он блокирует, ожидая, пока другой поток не вызовет release().

Семафоры также поддерживают протокол управления контекстом.

class threading.Semaphore(value=1)

Класс реализует объекты семафора. Семафор управляет атомным счетчиком, представляющим количество release() вызовов минус число acquire() вызовов плюс начальное значение. При необходимости метод acquire() блокирует до тех пор, пока не сможет возвращает без отрицательного значения счетчика. Если значение не задано, по умолчанию value значение 1.

Необязательный аргумент дает начальное value для внутреннего счетчика; значение по умолчанию - 1. Если заданное value меньше 0, ValueError поднимается.

Изменено в версии 3.3: Изменен с функции фабрики на класс.

acquire(blocking=True, timeout=None)

Получить семафор.

При вызове без аргументов:

  • Если внутренний счетчик на входе больше нуля, уменьшите его на единицу и немедленно возвращает True.
  • Если на входе внутренний счетчик равен нулю, блокировать его до пробуждения вызовом release(). После пробуждения (и счетчик больше 0) уменьшите счетчик на 1 и возвращает True. Каждый звонок в поток пробуждает ровно один release(). Не следует полагаться на порядок, в котором потоки пробуждаются.

Если для blocking задано значение false, не блокировать. Если вызов без аргумента блокируется, возвращает False немедленно; в противном случае делайте то же самое, что при вызове без аргументов, и возвращает True.

При вызове с timeout, отличным от None, он блокируется максимум на timeout секунд. Если получение не завершилось успешно в этом интервале, возвращает False. Возвращает True иначе.

Изменено в версии 3.2: Параметр timeout является новым.

release()

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

class threading.BoundedSemaphore(value=1)

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

Изменено в версии 3.3: Изменен с функции фабрики на класс.

Пример Semaphore

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

maxconnections = 5
# ...
pool_sema = BoundedSemaphore(value=maxconnections)

После создания рабочие потоки вызывают методы получения и освобождения семафора, когда им необходимо подключиться к серверу:

with pool_sema:
    conn = connectdb()
    try:
        # ... использовать соединение ...
    finally:
        conn.close()

Использование ограниченного семафора уменьшает вероятность того, что ошибка программирования, которая приводит к освобождению семафора больше, чем он получен, останется незамеченной.

Объекты Event

Это один из простейших механизмов коммуникации между потоками: один поток сигнализирует о событии, а другие потоки ждут его.

Объект события управляет внутренним флагом, которому можно присвоить значение true с помощью метода set() и сбросить значение false с помощью метода clear(). Метод wait() блокирует до тех пор, пока флаг не станет истинным.

class threading.Event

Класс, реализующий объекты событий. Событие управляет флагом, которому можно присвоить значение true с помощью метода set() и сбросить значение false с помощью метода clear(). Метод wait() блокирует до тех пор, пока флаг не станет true. Флаг изначально имеет значение false.

Изменено в версии 3.3: Изменен с функции фабрики на класс.

is_set()

Возвращает True тогда и только тогда, когда внутренний флаг имеет значение true.

set()

Установить внутренний флаг в значение true. Все потоки, ожидающие, когда это станет правдой, пробуждаются. Потоки, которые вызывают wait() после того, как флаг имеет значение true, вообще не будут блокироваться.

clear()

Сбросить внутренний флаг на false. Впоследствии вызывающий потоки wait() будет блокировать до тех пор, пока set() не будет вызван, чтобы снова установить внутренний флаг в значение true.

wait(timeout=None)

Блокировать до тех пор, пока внутренний флаг не станет истинным. Если при вводе внутренний флаг имеет значение true, возвращает немедленно. В противном случае блокировать до тех пор, пока другой поток не вызовет set() установить флаг в значение true или пока не наступит дополнительный тайм-аут.

Когда аргумент timeout присутствует, а не None, он должен быть числом с плавающей запятой, указывающим тайм-аут для операции в секундах (или ее долях).

Этот метод возвращает True тогда и только тогда, когда внутренний флаг был установлен на true, либо до вызова ожидания, либо после запуска ожидания, поэтому он всегда будет возвращает True за исключением того, что задан тайм-аут, и операция истекает.

Изменено в версии 3.1: Ранее метод всегда возвращенный None.

Объекты Timer

Класс представляет действие, которое должно выполняться только по истечении определенного периода времени таймера. Timer - это подкласс Thread, который также служит примером создания пользовательских потоков.

Таймеры запускаются, как и потоки, путем вызова метода start(). Таймер может быть остановлен (до начала его действия) путем вызова метода cancel(). Интервал ожидания таймера перед выполнением его действия может не совпадать с интервалом, заданным пользователем.

Например:

def hello():
    print("hello, world")

t = Timer(30.0, hello)
t.start()  # через 30 секунд будет напечатано "hello, world"
class threading.Timer(interval, function, args=None, kwargs=None)

Создать таймер, который будет управлять function с аргументами args и ключевыми аргументами kwargs, после того, как секунды interval прошли. Если args None (по умолчанию), то будет используемый пустой список. Если kwargs равно None (по умолчанию), то будет используемый пустой словарь.

Изменено в версии 3.3: Изменен с функцией фабрикой на класс.

cancel()

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

Объекты Barrier

Добавлено в версии 3.2.

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

Барьер может быть повторно использован любое количество раз для одного и того же числа потоков.

В качестве примера приведем простой способ синхронизации поток клиента и сервера:

b = Barrier(2, timeout=5)

def server():
    start_server()
    b.wait()
    while True:
        connection = accept_connection()
        process_server_connection(connection)

def client():
    b.wait()
    while True:
        connection = make_connection()
        process_client_connection(connection)
class threading.Barrier(parties, action=None, timeout=None)

Создать объект-барьер для parties количества потоки. Предоставленный action является вызываемым, вызываемым одним из потоки, когда он освобождается. timeout - значение тайм-аута по умолчанию, если для метода wait() не указан ни один.

wait(timeout=None)

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

Возвращает значение - целое число в диапазоне от 0 до parties - 1, различное для каждого потока. Это может быть используемый для выбора поток для выполнения некоторых специальных работ, например,:

i = barrier.wait()
if i == 0:
    # Только один поток должен напечатать это
    print("passed the barrier")

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

Если время вызова истекло, барьер помещается в сломанный состояние.

Метод может вызвать исключение BrokenBarrierError, если барьер нарушен или сброшен во время ожидания поток.

reset()

Возвращает барьер по умолчанию, пустое состояние. Любые потоки, ожидающий его, получит BrokenBarrierError исключение.

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

abort()

Поместить барьера в разбитое состояние. Это приводит к сбою всех активных или будущих вызовов wait() с BrokenBarrierError. Используйте это, например, если один из потоки должен прерваться, чтобы избежать блокировки приложения.

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

parties

Количество потоков, необходимых для прохождения барьера.

n_waiting

Число потоков, ожидающих в настоящее время в барьере.

broken

Логическое значение, True, если барьер находится в разбитом состояние.

exception threading.BrokenBarrierError

Исключение — подкласс RuntimeError, возникает при сбросе или разрыве объекта Barrier.

Использование блокировок, условий и семафоров в with инструкцией

Все объекты, предоставляемые этим модулем, которые имеют методы acquire() и release(), могут быть используемый в качестве менеджеров контекст для with инструкция. Метод acquire() вызывается при вводе блока, а release() - при выходе из блока. Следовательно, следующий фрагмент:

with some_lock:
    # сделать что-нибудь...

эквивалентно:

some_lock.acquire()
try:
    # сделать что-нибудь...
finally:
    some_lock.release()

В настоящее время Lock, RLock, Condition, Semaphore и объекты BoundedSemaphore могут быть используемый как менеджерами контекста инструкцией with.