threading — Параллелизм на основе потоков

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


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

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

См.также

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

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

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

Примечание

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

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

Availability: это не Emscripten, это был не я.

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

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

threading.active_count()

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

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

threading.current_thread()

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

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

threading.excepthook(args, /)

Обработать неперехваченное исключение, вызванное Thread.run().

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

  • exc_type: Тип исключения.

  • exc_value: Исключительное значение, может быть None.

  • exc_traceback: Обратная трассировка исключения, может быть None.

  • поток: Поток, который вызвал исключение, может быть 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()

Возвращает «идентификатор потока» текущего потока. Это ненулевое целое число. Его значение не имеет прямого значения; оно предназначено в качестве волшебного файла 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. Функция * будет передана в sys.settrace() для каждого потока перед вызовом его метода run().

threading.gettrace()

Получите функцию трассировки, заданную параметром settrace().

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

threading.setprofile(func)

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

threading.getprofile()

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

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

threading.stack_size([size])

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

Availability: Окна, pthreads.

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

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

threading.TIMEOUT_MAX

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

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

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

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

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

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

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

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

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

class threading.local

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

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

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

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

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

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

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

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

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

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

Примечание

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

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

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

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

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

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

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

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

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

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

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

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

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

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

start()

Запустите действие потока.

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

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

run()

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

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

Использование list или tuple в качестве аргумента 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()

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

Не рекомендуется, начиная с версии 3.10.

ident

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

native_id

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

Примечание

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

Availability: 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()

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

Не рекомендуется, начиная с версии 3.10.

Блокировка объектов

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

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

Блокировки также поддерживают context management protocol.

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

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

class threading.Lock

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

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

acquire(blocking=True, timeout=-1)

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

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

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

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

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

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

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

release()

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

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

При вызове разблокированной блокировки генерируется RuntimeError.

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

locked()

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

Объекты RLock

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

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

Реентерабельные блокировки также поддерживают context management protocol.

class threading.RLock

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

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

acquire(blocking=True, timeout=-1)

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

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

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

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

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

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

release()

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

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

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

Объекты состояния

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

Переменная условия подчиняется context management protocol: используя инструкцию 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, если только заданный тайм-аут не истек, и в этом случае оно равно False.

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

wait_for(predicate, timeout=None)

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

Этот служебный метод может вызывать 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() и <<<3>).>>) release() .

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

Семафоры также поддерживают context management protocol.

class threading.Semaphore(value=1)

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

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

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

acquire(blocking=True, timeout=None)

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

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

  • Если внутренний счетчик при вводе больше нуля, уменьшите его на единицу и немедленно верните True.

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

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

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

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

release(n=1)

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

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

class threading.BoundedSemaphore(value=1)

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

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

Semaphore Пример

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

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

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

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

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

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

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

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

class threading.Event

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

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

is_set()

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

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

set()

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

clear()

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

wait(timeout=None)

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

Когда присутствует аргумент timeout, а не 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)

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

Изменено в версии 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)

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

wait(timeout=None)

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

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

i = barrier.wait()
if i == 0:
    # Only one thread needs to print this
    print("passed the barrier")

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

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

Этот метод может вызвать исключение 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 менеджеров контекста инструкций.

Вернуться на верх