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.
-
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 (использовать значение по умолчанию для платформы или конфигурации) либо быть положительным целым числом не меньше 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; в настоящее время в Python нет приоритетов и групп потоков, а потоки нельзя уничтожать, останавливать, приостанавливать, возобновлять или прерывать. Статические методы класса 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.Тот же результат можно получить, передав в
Threadв качестве аргумента args список или кортеж.Пример:
>>> 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 -
ID потока (
TID) — идентификатор, присвоенный этому потоку операционной системой (ядром). Это неотрицательное целое число илиNone, если поток ещё не запущен. См. функциюget_native_id(). Это значение можно использовать для уникальной идентификации данного потока в пределах всей системы (до завершения потока; после этого ОС может использовать значение повторно).Примечание
Как и идентификаторы процессов, 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, задаёт неограниченное ожидание. Если blocking равенFalse, указывать timeout запрещено.Возвращаемое значение равно
True, если блокировка успешно захвачена, иFalseв противном случае (например, если истёк тайм-аут).Изменено в версии 3.2: Добавлен параметр timeout.
Изменено в версии 3.2: Теперь захват блокировки может прерываться сигналами в POSIX, если это поддерживается базовой реализацией потоков.
-
release() -
Освободить блокировку. Этот метод может вызвать любой поток, а не только тот, который захватил блокировку.
Если блокировка заблокирована, перевести её в разблокированное состояние и вернуть управление. Если другие потоки заблокированы в ожидании разблокировки, разрешить продолжить работу ровно одному из них.
При вызове для разблокированной блокировки будет вызвано исключение
RuntimeError.Возвращаемого значения нет.
-
locked() -
Возвращает
True, если блокировка захвачена.
-
Объекты рекурсивных блокировок
Рекурсивная блокировка — это примитив синхронизации, который один и тот же поток может захватывать несколько раз. Помимо состояния «заблокирована/разблокирована», внутри используются понятия «поток-владелец» и «уровень рекурсии». В заблокированном состоянии блокировкой владеет какой-либо поток; в разблокированном состоянии она не принадлежит ни одному потоку.
Потоки вызывают метод acquire() блокировки, чтобы заблокировать её, и метод release(), чтобы разблокировать.
Примечание
Рекурсивные блокировки поддерживают протокол управления контекстом, поэтому рекомендуется использовать with вместо ручных вызовов acquire() и release() для захвата и освобождения блокировки в блоке кода.
В отличие от acquire()/release() блокировки Lock, пары вызовов acquire()/release() блокировки RLock могут быть вложенными. Только последний вызов 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()может привести к взаимной блокировке. Вместо прямых вызовов acquire/release рассмотрите использованиеRLockв качестве менеджера контекста.Изменено в версии 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. Все потоки, ожидающие, пока он станет равен 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.12/library/threading.html