Spec-Zone.ru › Python 3.11

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.

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

threading.enumerate()

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

threading.main_thread()

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

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

threading.settrace(func)

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

threading.gettrace()

Возвращает функцию трассировки, заданную функцией settrace().

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

threading.setprofile(func)

Задаёт функцию профилирования для всех потоков, запущенных из модуля threading. Для каждого потока функция func будет передана в 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).

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

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

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

threading.TIMEOUT_MAX

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

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

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

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

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

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

Локальные данные потока — это данные, значения которых зависят от потока. Для управления такими данными достаточно создать экземпляр 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. Фиктивные объекты потоков имеют ограниченные возможности: они всегда считаются активными и являются потоками-демонами, а к ним нельзя присоединиться с помощью метода join. Они никогда не удаляются, поскольку обнаружить завершение чужих потоков невозможно.

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 (по умолчанию), свойство демона наследуется от текущего потока.

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

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

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

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, после вызова join() необходимо вызвать is_alive(), чтобы определить, истекло ли время ожидания: если поток всё ещё активен, вызов 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 в противном случае (например, если истёк срок ожидания).

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

Метод не возвращает значения.

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

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

Переменная условия поддерживает протокол управления контекстом: инструкция 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 является устаревшим псевдонимом этого метода.

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

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

Семафор управляет внутренним счётчиком: каждый вызов 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(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. Все потоки, ожидающие установки этого значения, пробуждаются. Потоки, вызывающие wait() после того, как флаг принял значение true, не будут блокироваться.

clear()

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

wait(timeout=None)

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

Если аргумент 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)

Создаёт таймер, который через interval секунд вызовет function с позиционными аргументами args и именованными аргументами kwargs. Если 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.11/library/threading.html

Spec-Zone.ru

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