Spec-Zone.ru › Python 3.12

threading — Потоковое параллельное выполнение

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

Этот модуль строит интерфейсы потоков более высокого уровня поверх модуля более низкого уровня _thread.

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

См. также

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

queue предоставляет потокобезопасный интерфейс для обмена данными между запущенными потоками.

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

Примечание

В серии Python 2.x этот модуль содержал camelCase имена некоторых методов и функций. Они устарели начиная с Python 3.10, но все еще поддерживаются для совместимости с Python 2.5 и более ранними версиями.

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

Доступность: не Emscripten, не WASI.

Этот модуль не работает или недоступен на платформах WebAssembly wasm32-emscripten и wasm32-wasi. Дополнительную информацию см. в разделе Платформы WebAssembly.

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

threading.active_count()

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

Функция activeCount является устаревшим алиасом для этой функции.

threading.current_thread()

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

Функция currentThread является устаревшим алиасом для этой функции.

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.__excepthook__

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

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

threading.get_ident()

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

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

threading.get_native_id()

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

Доступность: Windows, FreeBSD, Linux, macOS, OpenBSD, NetBSD, AIX, DragonFlyBSD.

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

threading.enumerate()

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

threading.main_thread()

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

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

threading.settrace(func)

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

threading.settrace_all_threads(func)

Устанавливает функцию отслеживания для всех потоков, запущенных из модуля threading, и всех потоков Python, которые в настоящее время выполняются.

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

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

threading.gettrace()

Получает функцию отслеживания, установленную функцией settrace().

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

END_OF_DOCUMENT_MARKER
threading.setprofile(func)

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

threading.setprofile_all_threads(func)

Установите функцию профилирования для всех потоков, запущенных из модуля threading, и всех активных потоков Python.

Функция func будет передана в sys.setprofile() для каждого потока перед вызовом его метода run().

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

threading.getprofile()

Получить функцию профилирования, установленную с помощью setprofile().

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

threading.stack_size([size])

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

Доступность: Windows, pthreads.

Платформы Unix с поддержкой POSIX-потоков.

В этом модуле также определена следующая константа:

threading.TIMEOUT_MAX

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

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

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

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

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

Данные, локальные для потока

Данные, локальные для потока, — это данные, значения которых специфичны для каждого потока. Для управления данными, локальными для потока, просто создайте экземпляр local (или подкласс) и сохраните атрибуты в нём:

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

Значения экземпляра будут разными для разных потоков.

class threading.local

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

Для получения более подробной информации и расширенных примеров обратитесь к строке документации модуля _threading_local: Lib/_threading_local.py.

Объекты потоков

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

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

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

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

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

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

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

Примечание

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

Существует объект «главный поток»; он соответствует исходному потоку управления в программе Python. Он не является потоком-демоном.

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

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

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

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

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

name — имя потока. По умолчанию генерируется уникальное имя в формате «Поток-N», где N — небольшое десятичное число, или «Поток-N (целевой)», где «целевой» — target.__name__, если указан аргумент target.

args — список или кортеж аргументов для вызова целевого объекта. По умолчанию ().

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

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

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

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

Изменено в версии 3.10: Используйте имя target, если аргумент name опущен.

start()

Запустить активность потока.

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

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

run()

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

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

Использование списка или кортежа в качестве аргумента args, переданного в Thread, может обеспечить тот же эффект.

Пример:

>>> from threading import Thread
>>> t = Thread(target=print, args=[1])
>>> t.run()
1
>>> t = Thread(target=print, args=(1,))
>>> t.run()
1
join(timeout=None)

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

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

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

Поток можно присоединять многократно.

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

name

Строка, используемая только для идентификации. Она не имеет семантического значения. Несколько потоков могут иметь одинаковое имя. Начальное имя устанавливается конструктором.

getName()
setName()

Устаревший API-интерфейс для получения/установки name; используйте его напрямую как свойство вместо этого.

Устарело начиная с версии 3.10.

ident

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

native_id

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

Примечание

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

Доступность: Windows, FreeBSD, Linux, macOS, OpenBSD, NetBSD, AIX, DragonFlyBSD.

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

is_alive()

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

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

daemon

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

Весь процесс Python завершается, когда не остается активных не-демонических потоков.

isDaemon()
setDaemon()

Устаревший API-интерфейс для получения/установки daemon; используйте его напрямую как свойство вместо этого.

Устарело начиная с версии 3.10.

Объекты блокировки

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

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

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

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

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

class threading.Lock

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

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

acquire(blocking=True, timeout=-1)

Получить блокировку, блокирующим или неблокирующим способом.

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

При вызове с аргументом blocking, установленным в False, не блокировать. Если вызов с blocking, установленным в 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() для его разблокировки.

Примечание

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

Пары вызовов acquire()/release() замка RLock могут быть вложены, в отличие от пар acquire()/release() замка Lock. Только окончательный вызов release() (вызов release() самого внешнего пары) сбрасывает замок в разблокированное состояние и позволяет другому потоку, заблокированному в acquire(), продолжить работу.

Вызовы acquire()/release() должны использоваться парами: каждый захват должен иметь освобождение в потоке, который захватил замок. Отсутствие вызовов release столько раз, сколько замок был захвачен, может привести к тупику.

class threading.RLock

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

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

acquire(blocking=True, timeout=-1)

Получить замок, блокируя или не блокируя.

См. также

Использование RLock как менеджера контекста

Рекомендуется использовать вместо ручных вызовов acquire() и release() всякий раз, когда это практично.

При вызове с аргументом blocking, установленным в True (значение по умолчанию):

  • Если ни один поток не владеет замком, захватить замок и вернуть значение немедленно.
  • Если другим потоком владеет замок, заблокироваться, пока не удастся захватить замок, или timeout, если установлено положительное значение с плавающей точкой.
  • Если тот же поток владеет замком, захватить замок снова и вернуть значение немедленно. Это различие между Lock и RLock; Lock обрабатывает этот случай так же, как и предыдущий, блокируясь, пока замок не будет захвачен.

При вызове с аргументом blocking, установленным в False:

  • Если ни один поток не владеет замком, захватить замок и вернуть значение немедленно.
  • Если другим потоком владеет замок, вернуть значение немедленно.
  • Если тот же поток владеет замком, захватить замок снова и вернуть значение немедленно.

Во всех случаях, если потоку удалось захватить замок, вернуть True. Если потоку не удалось захватить замок (т.е. если не блокируется или истек срок ожидания), вернуть False.

Если вызывать несколько раз, отсутствие вызова release() столько раз может привести к тупику. Рассмотрите возможность использования RLock в качестве менеджера контекста вместо прямого вызова acquire/release.

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

release()

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

Вызывайте этот метод только тогда, когда вызывающий поток владеет замком. Если этот метод вызван, когда замок не захвачен, возникает RuntimeError.

Значение возврата отсутствует.

Объекты условия

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

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

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

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

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

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

# Consume one item
with cv:
    while not an_item_is_available():
        cv.wait()
    get_an_available_item()

# Produce one item
with cv:
    make_an_item_available()
    cv.notify()

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

# Consume an item
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)

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

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

Без учёта таймаута, вызов этого метода примерно эквивалентен следующему:

while not predicate():
    cv.wait()

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

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

notify(n=1)

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

Этот метод разбудит не более n потоков, ожидающих условной переменной; это ничто, если нет ожидающих потоков.

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

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

notify_all()

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

Метод notifyAll является устаревшим псевдонимом для этого метода.

Объекты семафоров

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

Семафор управляет внутренней переменной, которая уменьшается на 1 при каждом вызове acquire() и увеличивается на 1 при каждом вызове 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)

Приобретение семафора.

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

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

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

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

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

release(n=1)

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

Изменено в версии 3.9: Добавлен параметр n для одновременного пробуждения нескольких ожидающих нитей.

class threading.BoundedSemaphore(value=1)

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

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

Semaphore Пример

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

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

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

with pool_sema:
    conn = connectdb()
    try:
        # ... use connection ...
    finally:
        conn.close()

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

Объекты событий

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

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

class threading.Event

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

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

is_set()

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

Метод isSet является устаревшим алиасом для этого метода.

set()

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

clear()

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

wait(timeout=None)

Блокируется, пока внутренний флаг равен false и тайм-аут, если задан, не истек. Возвращаемое значение представляет причину возврата этого блокирующего метода; True если возвращается потому, что внутренний флаг установлен в true, или False если задан тайм-аут и внутренний флаг не стал true в течение заданного времени ожидания.

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

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

Объекты таймеров

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

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

Например:

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

t = Timer(30.0, hello)
t.start()  # after 30 seconds, "hello, world" will be printed
class threading.Timer(interval, function, args=None, kwargs=None)

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

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

cancel()

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

Объекты барьеров

Добавлен в версии 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:
    # Only one thread needs to print this
    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:
    # do something...

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

some_lock.acquire()
try:
    # do something...
finally:
    some_lock.release()

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

© 2001–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/library/threading.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API