threading – Параллелизм на основе потоков
Исходный код: Lib/threading.py
Этот модуль создает высокоуровневые интерфейсы для потоков на основе низкоуровневого модуля _thread.
Доступность: не WASI.
Этот модуль не работает или недоступен на WebAssembly. См. платформы WebAssembly для получения дополнительной информации.
IntroductionВведение
Модуль threading предоставляет способ одновременного выполнения нескольких потоков (меньших единиц процесса) в рамках одного процесса. Он позволяет создавать и управлять потоками, что дает возможность выполнять задачи параллельно с разделением памяти. Потоки особенно полезны для задач, связанных с вводом-выводом, таких как файловые операции или сетевые запросы, где большая часть времени тратится на ожидание внешних ресурсов.
Типичный вариант использования threading включает управление пулом рабочих потоков, которые могут одновременно обрабатывать несколько задач. Вот базовый пример создания и запуска потоков с помощью Thread:
import threading
import time
def crawl(link, delay=3):
print(f"crawl started for {link}")
time.sleep(delay) # Блокирующий ввод-вывод (имитация сетевого запроса)
print(f"crawl ended for {link}")
links = [
"https://python.org",
"https://docs.python.org",
"https://peps.python.org",
]
# Запустить потоки для каждой ссылки
threads = []
for link in links:
# Использование `args` для передачи позиционных аргументов и `kwargs` для именованных аргументов
t = threading.Thread(target=crawl, args=(link,), kwargs={"delay": 2})
threads.append(t)
# Запустить каждый поток
for t in threads:
t.start()
# Ожидать завершения всех потоков
for t in threads:
t.join()
Изменено в версии 3.7: Этот модуль раньше был необязательным, теперь он всегда доступен.
Смотрите также
concurrent.futures.ThreadPoolExecutor предоставляет высокоуровневый интерфейс для отправки задач в фоновый поток без блокировки выполнения вызывающего потока, при этом позволяя получать их результаты по мере необходимости.
queue предоставляет потокобезопасный интерфейс для обмена данными между работающими потоками.
asyncio предлагает альтернативный подход к достижению параллелизма на уровне задач без необходимости использования нескольких системных потоков.
Примечание
В серии Python 2.x этот модуль содержал camelCase имена для некоторых методов и функций. Они объявлены устаревшими, начиная с Python 3.10, но все еще поддерживаются для совместимости с Python 2.5 и ниже.
Особенность реализации CPython: В CPython из-за глобальной блокировки интерпретатора (GIL) только один поток может одновременно выполнять код Python (хотя некоторые библиотеки, ориентированные на производительность, могут обойти это ограничение). Если вы хотите, чтобы ваше приложение лучше использовало вычислительные ресурсы многоядерных машин, рекомендуется использовать multiprocessing или concurrent.futures.ProcessPoolExecutor. Однако модель потоков по-прежнему подходит, если вы хотите одновременно выполнять несколько задач, связанных с вводом-выводом.
GIL and performance considerationsGIL и вопросы производительности
В отличие от модуля multiprocessing, который использует отдельные процессы для обхода глобальной блокировки интерпретатора (GIL), модуль threading работает в рамках одного процесса, то есть все потоки разделяют общее адресное пространство. Однако GIL ограничивает прирост производительности при использовании потоков для задач, связанных с процессором, поскольку только один поток может одновременно выполнять байт-код Python. Несмотря на это, потоки остаются полезным инструментом для достижения параллелизма во многих сценариях.
Начиная с Python 3.13, сборки со свободной потоковой обработкой могут отключать GIL, обеспечивая истинное параллельное выполнение потоков, но эта функция недоступна по умолчанию (см. PEP 703).
ReferenceСсылка
Этот модуль определяет следующие функции:
- 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: 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()¶
Возвращает «идентификатор потока» текущего потока. Это ненулевое целое число. Его значение не имеет прямого смысла; оно предназначено в качестве «магического cookie», который можно использовать, например, для индексации словаря данных, специфичных для потока. Идентификаторы потоков могут быть переиспользованы, когда один поток завершается и создаётся другой.
Добавлено в версии 3.3.
- threading.get_native_id()¶
Возвращает нативный целочисленный идентификатор потока, назначенный ядром для текущего потока. Это неотрицательное целое число. Его значение может использоваться для уникальной идентификации данного потока в масштабе системы (до завершения потока, после чего значение может быть переиспользовано операционной системой).
Доступность: Windows, FreeBSD, Linux, macOS, OpenBSD, NetBSD, AIX, DragonFlyBSD, GNU/kFreeBSD, Solaris.
Добавлено в версии 3.8.
Изменено в версии 3.13: Добавлена поддержка GNU/kFreeBSD.
Изменено в версии 3.15: Добавлена поддержка Solaris.
- 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 они представляют собой отдельные объекты. Класс Thread в Python поддерживает подмножество поведения класса Thread в Java; в настоящее время отсутствуют приоритеты, группы потоков, а потоки нельзя уничтожить, остановить, приостановить, возобновить или прервать. Статические методы класса Thread в Java при реализации сопоставляются функциям уровня модуля.
Все методы, описанные ниже, выполняются атомарно.
Thread-local dataДанные, локальные для потока
Данные, локальные для потока – это данные, значения которых привязаны к конкретному потоку. Если у вас есть данные, которые должны быть локальными для потока, создайте объект local и используйте его атрибуты:
>>> mydata = local()
>>> mydata.number = 42
>>> mydata.number
42
Также можно получить доступ к словарю объекта local:
>>> mydata.__dict__
{'number': 42}
>>> mydata.__dict__.setdefault('widgets', [])
[]
>>> mydata.widgets
[]
Если обратиться к данным в другом потоке:
>>> log = []
>>> def f():
... items = sorted(mydata.__dict__.items())
... log.append(items)
... mydata.number = 11
... log.append(mydata.number)
>>> import threading
>>> thread = threading.Thread(target=f)
>>> thread.start()
>>> thread.join()
>>> log
[[], 11]
мы получим другие данные. Более того, изменения, внесённые в другом потоке, не влияют на данные, видимые в этом потоке:
>>> mydata.number
42
Разумеется, значения, получаемые из объекта local, включая его атрибут __dict__, относятся к тому потоку, который был текущим на момент чтения атрибута. По этой причине обычно не следует сохранять эти значения между потоками, поскольку они применимы только к тому потоку, из которого получены.
Можно создать собственные объекты local, унаследовав от класса local:
>>> class MyLocal(local):
... number = 2
... def __init__(self, /, **kw):
... self.__dict__.update(kw)
... def squared(self):
... return self.number ** 2
Это может быть полезно для поддержки значений по умолчанию, методов и инициализации. Обратите внимание, что если определить метод __init__(), он будет вызываться каждый раз, когда объект local используется в отдельном потоке. Это необходимо для инициализации словаря каждого потока.
Теперь, если создать объект local:
>>> mydata = MyLocal(color='red')
мы получим число по умолчанию:
>>> mydata.number
2
начальный цвет:
>>> mydata.color
'red'
>>> del mydata.color
И метод, работающий с данными:
>>> mydata.squared()
4
Как и прежде, мы можем получить доступ к данным в отдельном потоке:
>>> log = []
>>> thread = threading.Thread(target=f)
>>> thread.start()
>>> thread.join()
>>> log
[[('color', 'red')], 11]
не затрагивая данные этого потока:
>>> mydata.number
2
>>> mydata.color
Traceback (most recent call last):
...
AttributeError: 'MyLocal' object has no attribute 'color'
Обратите внимание, что подклассы могут определять __slots__, но они не являются локальными для потока. Они разделяются между потоками:
>>> class MyLocal(local):
... __slots__ = 'number'
>>> mydata = MyLocal()
>>> mydata.number = 42
>>> mydata.color = 'red'
Итак, отдельный поток:
>>> thread = threading.Thread(target=f)
>>> thread.start()
>>> thread.join()
влияет на то, что мы видим:
>>> mydata.number
11
- class threading.local¶
Класс, представляющий данные, локальные для потока.
Thread objectsОбъекты потоков
Класс Thread представляет действие, которое выполняется в отдельном потоке управления. Есть два способа задать действие: передав вызываемый объект конструктору или переопределив метод run() в подклассе. Никакие другие методы (кроме конструктора) не должны переопределяться в подклассе. Другими словами, только переопределяйте методы __init__() и run() этого класса.
После создания объекта потока его действие должно быть запущено вызовом метода start() потока. Это вызывает метод run() в отдельном потоке управления.
После запуска действия потока он считается «живым». Он перестаёт быть живым, когда его метод run() завершается – либо нормально, либо из-за необработанного исключения. Метод is_alive() проверяет, жив ли поток.
Другие потоки могут вызывать метод join() некоторого потока. Это блокирует вызывающий поток до тех пор, пока поток, чей метод join() был вызван, не завершится.
Поток имеет имя. Имя может быть передано конструктору, а также прочитано или изменено через атрибут name.
Если метод run() вызывает исключение, вызывается threading.excepthook() для его обработки. По умолчанию threading.excepthook() молча игнорирует SystemExit.
Поток может быть помечен как «фоновый поток». Значение этого флага в том, что вся программа Python завершается, когда остаются только фоновые потоки. Начальное значение наследуется от создающего потока. Флаг можно установить через свойство daemon или аргумент конструктора daemon.
Примечание
Фоновые потоки при завершении работы программы останавливаются принудительно. Их ресурсы (такие как открытые файлы, транзакции базы данных и т.п.) могут быть освобождены некорректно. Если вы хотите, чтобы ваши потоки завершались корректно, сделайте их не фоновыми и используйте подходящий механизм сигнализации, например Event.
Существует объект «главный поток»; он соответствует начальному потоку управления в программе Python. Он не является фоновым потоком.
Существует возможность создания «фиктивных объектов потоков». Это объекты потоков, соответствующие «внешним потокам», то есть потокам управления, запущенным за пределами модуля threading, например непосредственно из кода на C. Фиктивные объекты потоков имеют ограниченную функциональность; они всегда считаются живыми и фоновыми, и к ним нельзя присоединиться. Они никогда не удаляются, поскольку невозможно обнаружить завершение внешних потоков.
- class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None, context=None)¶
Этот конструктор всегда следует вызывать с именованными аргументами. Аргументы:
group должен быть
None; зарезервирован для будущих расширений, когда будет реализован классThreadGroup.target – это вызываемый объект, который будет вызван методом
run(). По умолчаниюNone, то есть ничего не вызывается.name – это имя потока. По умолчанию создаётся уникальное имя вида «Thread-N», где N – небольшое десятичное число, или «Thread-N (target)», где «target» – это
target.__name__, если указан аргумент target.args – это список или кортеж аргументов для вызова целевого объекта. По умолчанию
().kwargs – это словарь именованных аргументов для вызова целевого объекта. По умолчанию
{}.Если не
None, daemon явно указывает, является ли поток фоновым. ЕслиNone(по умолчанию), свойство фоновости наследуется от текущего потока.context – это значение
Context, которое используется при запуске потока. Значение по умолчанию –None, что означает, что поведение определяется флагомsys.flags.thread_inherit_context. Если флаг истинен, потоки будут запускаться с копией контекста вызывающего кодаstart(). Если ложен – с пустым контекстом. Чтобы явно запустить с пустым контекстом, передайте новый экземплярContext(). Чтобы явно запустить с копией текущего контекста, передайте значение изcopy_context(). По умолчанию флаг истинен для сборок со свободными потоками (free-threaded builds) и ложен в остальных случаях.Если подкласс переопределяет конструктор, он должен обязательно вызвать конструктор базового класса (
Thread.__init__()) перед любыми другими действиями с потоком.Изменено в версии 3.3: Добавлен параметр daemon.
Изменено в версии 3.10: Используется имя target, если аргумент name опущен.
Изменено в версии 3.14: Добавлен параметр context.
- start()¶
Запускает действие потока.
Он должен быть вызван не более одного раза для каждого объекта потока. Он обеспечивает вызов метода
run()объекта в отдельном потоке управления.Этот метод вызовет
RuntimeError, если он вызван более одного раза для одного и того же объекта потока.Если поддерживается, устанавливает имя потока операционной системы в
threading.Thread.name. Имя может быть усечено в зависимости от ограничений на имена потоков операционной системы.Изменено в версии 3.14: Устанавливает имя потока операционной системы.
- run()¶
Метод, представляющий активность потока.
Этот метод можно переопределить в подклассе. Стандартный метод
run()вызывает вызываемый объект, переданный в конструктор объекта как аргумент target, если он есть, с позиционными и именованными аргументами, взятыми из аргументов args и kwargs соответственно.Использование списка или кортежа в качестве аргумента args, который передаётся в
Thread, может дать тот же эффект.Пример:
python>>> 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()для потока до его запуска; попытки сделать это возбуждают то же исключение.Если предпринята попытка присоединиться к работающему демоническому потоку на поздних этапах финализации Python,
join()возбуждаетPythonFinalizationError.Изменено в версии 3.14: Может возбуждать
PythonFinalizationError.Изменено в версии 3.15: Принимает любое вещественное число в качестве timeout, а не только целое или число с плавающей запятой.
- name¶
Строка, используемая только для идентификации. Она не несёт смысловой нагрузки. Несколько потоков могут иметь одно и то же имя. Начальное имя задаётся конструктором.
На некоторых платформах имя потока устанавливается на уровне операционной системы при запуске потока, так что оно видно в диспетчерах задач. Это имя может быть усечено до системного ограничения (например, 15 байт в Linux или 63 байта в macOS).
Изменения name отражаются на уровне ОС только при переименовании текущего выполняющегося потока. (Установка атрибута 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 завершается, когда не остаётся ни одного живого недемонического потока.
Lock objectsОбъекты блокировок
Примитивная блокировка – это примитив синхронизации, который не принадлежит какому-либо конкретному потоку, когда он заблокирован. В Python в настоящее время это самый низкоуровневый доступный примитив синхронизации, реализованный непосредственно модулем расширения _thread.
Примитивная блокировка находится в одном из двух состояний: «заблокирована» или «разблокирована». Она создаётся в разблокированном состоянии. У неё есть два основных метода: acquire() и release(). Когда состояние разблокировано, acquire() переводит состояние в заблокированное и немедленно возвращается. Когда состояние заблокировано, acquire() блокируется до тех пор, пока вызов release() в другом потоке не переведёт его в разблокированное, затем вызов acquire() снова устанавливает заблокированное состояние и возвращается. Метод release() следует вызывать только в заблокированном состоянии; он переводит состояние в разблокированное и немедленно возвращается. Если предпринимается попытка освободить разблокированную блокировку, будет возбуждено RuntimeError.
Блокировки также поддерживают протокол контекстного менеджера.
Когда несколько потоков заблокированы в acquire() в ожидании перехода состояния в разблокированное, только один поток продолжает работу, когда вызов release() переводит состояние в разблокированное; какой именно из ожидающих потоков продолжит, не определено и может различаться в разных реализациях.
Все методы выполняются атомарно.
- class threading.Lock¶
Класс, реализующий объекты примитивной блокировки. Как только поток захватил блокировку, последующие попытки захватить её блокируются до тех пор, пока она не будет освобождена; любой поток может её освободить.
Изменено в версии 3.13:
Lockтеперь является классом. В более ранних версиях PythonLockбыла фабричной функцией, которая возвращала экземпляр внутреннего закрытого типа блокировки.- acquire(blocking=True, timeout=-1)¶
Захватить блокировку (блокирующую или неблокирующую).
При вызове с аргументом blocking, установленным в
True(значение по умолчанию), блокирует выполнение до тех пор, пока блокировка не будет разблокирована, затем устанавливает её в заблокированное состояние и возвращаетTrue.При вызове с аргументом blocking, установленным в
False, не блокирует выполнение. Если вызов с blocking, установленным вTrue, привёл бы к блокировке, немедленно возвращаетFalse; в противном случае устанавливает блокировку в заблокированное состояние и возвращаетTrue.При вызове с аргументом timeout, установленным в положительное значение, блокирует выполнение не более чем на количество секунд, заданное timeout, и до тех пор, пока блокировку не удастся захватить. Аргумент timeout, равный
-1, задаёт неограниченное ожидание. Запрещено указывать timeout, когда blocking равноFalse.Возвращаемое значение равно
True, если блокировка успешно захвачена, иFalseв противном случае (например, если истёк timeout).Изменено в версии 3.2: Параметр timeout является новым.
Изменено в версии 3.2: Захват блокировки теперь может быть прерван сигналами на POSIX, если базовая реализация потоков поддерживает это.
Изменено в версии 3.14: Захват блокировки теперь может быть прерван сигналами на Windows.
Изменено в версии 3.15: Принимает любое вещественное число в качестве timeout, а не только целое или число с плавающей запятой.
- release()¶
Освобождает блокировку. Этот метод может быть вызван из любого потока, а не только из того, который захватил блокировку.
Если блокировка заблокирована, переводит её в разблокированное состояние и возвращается. Если какие-либо другие потоки заблокированы в ожидании разблокировки, ровно одному из них разрешается продолжить.
При вызове на разблокированной блокировке возбуждается
RuntimeError.Возвращаемое значение отсутствует.
- locked()¶
Возвращает
True, если блокировка захвачена.
RLock objectsОбъекты RLock
Реентерабельная блокировка – это примитив синхронизации, который может быть многократно захвачен одним и тем же потоком. Внутри она использует концепции «владеющего потока» и «уровня рекурсии» в дополнение к состоянию заблокирован/разблокирован, используемому примитивными блокировками. В заблокированном состоянии какой-то поток владеет блокировкой; в разблокированном состоянии ни один поток ею не владеет.
Потоки вызывают метод acquire() блокировки, чтобы заблокировать её, и метод release(), чтобы разблокировать.
Примечание
Реентерабельные блокировки поддерживают протокол контекстного менеджера, поэтому рекомендуется использовать with вместо ручного вызова acquire() и release() для управления захватом и освобождением блокировки в блоке кода.
Пары вызовов acquire()/release() у RLock могут быть вложенными, в отличие от acquire()/release() у Lock. Только последний release() (release() самой внешней пары) сбрасывает блокировку в разблокированное состояние и позволяет другому потоку, заблокированному в acquire(), продолжить.
acquire()/release() должны использоваться парами: каждый вызов acquire должен иметь соответствующий release в потоке, захватившем блокировку. Если release не вызывается столько же раз, сколько блокировка была захвачена, это может привести к взаимоблокировке.
- class threading.RLock¶
Этот класс реализует объекты реентерабельной блокировки. Реентерабельная блокировка должна освобождаться тем же потоком, который её захватил. Как только поток захватил реентерабельную блокировку, этот же поток может захватить её снова без блокировки; поток должен освободить её по одному разу за каждый захват.
Обратите внимание, что
RLockна самом деле является фабричной функцией, которая возвращает экземпляр наиболее эффективной версии конкретного класса RLock, поддерживаемой платформой.- acquire(blocking=True, timeout=-1)¶
Захватить блокировку (блокирующую или неблокирующую).
Смотрите также
- Использование RLock в качестве менеджера контекста
Рекомендуется вместо ручных вызовов
acquire()иrelease(), когда это осуществимо.
При вызове с аргументом blocking, установленным в
True(значение по умолчанию):Если никакой поток не владеет блокировкой, захватить блокировку и немедленно вернуться.
Если другой поток владеет блокировкой, блокироваться, пока не удастся захватить блокировку, или пока не истечёт timeout, если он задан положительным значением.
Если тот же поток владеет блокировкой, захватить блокировку снова и немедленно вернуться. В этом состоит различие между
LockиRLock;Lockобрабатывает этот случай так же, как предыдущий, блокируясь до тех пор, пока блокировку не удастся захватить.
При вызове с аргументом blocking, установленным в
False:Если никакой поток не владеет блокировкой, захватить блокировку и немедленно вернуться.
Если другой поток владеет блокировкой, немедленно вернуться.
Если тот же поток владеет блокировкой, захватить блокировку снова и немедленно вернуться.
Во всех случаях, если поток смог захватить блокировку, вернуть
True. Если поток не смог захватить блокировку (т.е. если не блокирующий режим или истекло время ожидания), вернутьFalse.Если вызывается несколько раз, невызов
release()столько же раз может привести к взаимоблокировке. Рассмотрите возможность использованияRLockв качестве менеджера контекста вместо прямого вызова acquire/release.Изменено в версии 3.2: Параметр timeout добавлен.
Изменено в версии 3.15: Принимает любое вещественное число в качестве timeout, а не только целое или число с плавающей запятой.
- release()¶
Освобождает блокировку, уменьшая уровень рекурсии. Если после уменьшения он становится нулевым, сбрасывает блокировку в разблокированное состояние (не принадлежащую ни одному потоку), и если какие-либо другие потоки заблокированы в ожидании разблокировки блокировки, позволяет ровно одному из них продолжить. Если после уменьшения уровень рекурсии всё ещё ненулевой, блокировка остаётся захваченной и принадлежащей вызывающему потоку.
Вызывайте этот метод только тогда, когда вызывающий поток владеет блокировкой. Если этот метод вызывается, когда блокировка не захвачена, возбуждается
RuntimeError.Возвращаемое значение отсутствует.
- locked()¶
Возвращает логическое значение, указывающее, заблокирован ли этот объект в данный момент.
Добавлено в версии 3.14.
Condition objectsОбъекты условия
Переменная условия всегда связана с какой-либо блокировкой; эту блокировку можно передать, или она будет создана по умолчанию. Передача блокировки полезна, когда несколько переменных условия должны использовать одну и ту же блокировку. Блокировка является частью объекта условия: вам не нужно отслеживать её отдельно.
Переменная условия подчиняется протоколу менеджера контекста: использование оператора with захватывает связанную блокировку на время выполнения вложенного блока. Методы acquire() и release() также вызывают соответствующие методы связанной блокировки.
Другие методы должны вызываться при удерживании связанной блокировки. Метод wait() освобождает блокировку, а затем блокируется до тех пор, пока другой поток не пробудит его вызовом notify() или notify_all(). После пробуждения wait() повторно захватывает блокировку и возвращается. Также можно указать время ожидания.
Метод notify() пробуждает один из потоков, ожидающих переменную условия, если таковые есть. Метод notify_all() пробуждает все потоки, ожидающие переменную условия.
Примечание: методы notify() и notify_all() не освобождают блокировку; это означает, что пробуждённый поток или потоки не вернутся из своего вызова wait() немедленно, а только когда поток, вызвавший notify() или notify_all(), наконец освободит владение блокировкой.
Типичный стиль программирования с использованием переменных условия использует блокировку для синхронизации доступа к некоторому общему состоянию; потоки, заинтересованные в определённом изменении состояния, вызывают wait() многократно, пока не увидят желаемое состояние, в то время как потоки, изменяющие состояние, вызывают notify() или notify_all(), когда они изменяют состояние таким образом, что оно может оказаться желаемым для одного из ожидающих. Например, следующий код представляет собой общую ситуацию производителя-потребителя с неограниченной ёмкостью буфера.
# Потребить один элемент
with cv:
while not an_item_is_available():
cv.wait()
get_an_available_item()
# Произвести один элемент
with cv:
make_an_item_available()
cv.notify()
Цикл while, проверяющий условие приложения, необходим, потому что wait() может вернуться после сколь угодно долгого времени, и условие, которое вызвало вызов notify(), может уже не выполняться. Это присуще многопоточному программированию. Метод wait_for() можно использовать для автоматизации проверки условия и упрощения вычисления тайм-аутов:
# Потребить элемент
with cv:
cv.wait_for(an_item_is_available)
get_an_available_item()
Чтобы выбрать между notify() и notify_all(), подумайте, может ли одно изменение состояния быть интересно только одному или нескольким ожидающим потокам. Например, в типичной ситуации производителя-потребителя добавление одного элемента в буфер требует пробуждения только одного потока-потребителя.
- class threading.Condition(lock=None)¶
Этот класс реализует объекты условных переменных. Условная переменная позволяет одному или нескольким потокам ждать, пока их не уведомит другой поток.
Если аргумент lock задан и не равен
None, он должен быть объектомLockилиRLockи используется как базовая блокировка. В противном случае создаётся новый объектRLock, который и используется в качестве базовой блокировки.Изменено в версии 3.3: изменено с фабричной функции на класс.
- acquire(*args)¶
Захватывает базовую блокировку. Этот метод вызывает соответствующий метод на базовой блокировке; возвращаемое значение равно тому, что возвращает этот метод.
- release()¶
Освобождает базовую блокировку. Этот метод вызывает соответствующий метод на базовой блокировке; возвращаемого значения нет.
- locked()¶
Возвращает логическое значение, указывающее, заблокирован ли этот объект в данный момент.
Добавлено в версии 3.14.
- 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, если метод завершился по тайм-ауту.Если не учитывать тайм-аут, вызов этого метода примерно эквивалентен следующему коду:
pythonwhile not predicate(): cv.wait()Следовательно, применяются те же правила, что и для
wait(): блокировка должна быть захвачена при вызове и повторно захватывается при возврате. Предикат вычисляется при удерживаемой блокировке.Добавлено в версии 3.2.
- notify(n=1)¶
По умолчанию пробуждает один поток, ожидающий на этом условии, если таковой имеется. Если вызывающий поток не захватил блокировку при вызове этого метода, возбуждается
RuntimeError.Этот метод пробуждает не более n потоков, ожидающих условную переменную; если нет ожидающих потоков, ничего не делает.
Текущая реализация пробуждает ровно n потоков, если ожидает не менее n потоков. Однако полагаться на это поведение небезопасно. В будущей оптимизированной реализации иногда может пробуждаться больше n потоков.
Примечание: пробуждённый поток на самом деле не возвращается из своего вызова
wait(), пока не сможет заново захватить блокировку. Посколькуnotify()не освобождает блокировку, это должен сделать его вызывающий код.
- notify_all()¶
Пробуждает все потоки, ожидающие на этом условии. Этот метод действует как
notify(), но пробуждает все ожидающие потоки, а не один. Если вызывающий поток не захватил блокировку при вызове этого метода, возбуждаетсяRuntimeError.Метод
notifyAllявляется устаревшим псевдонимом для этого метода.
Semaphore objectsОбъекты семафоров
Это один из старейших примитивов синхронизации в истории компьютерных наук, изобретённый нидерландским учёным Эдсгером Дейкстрой (он использовал имена P() и V() вместо acquire() и release()).
Семафор управляет внутренним счётчиком, который уменьшается при каждом вызове acquire() и увеличивается при каждом вызове release(). Счётчик никогда не может стать меньше нуля; когда acquire() обнаруживает, что он равен нулю, он блокируется, ожидая, пока другой поток вызовет release().
Семафоры также поддерживают протокол управления контекстом.
- class threading.Semaphore(value=1)¶
Этот класс реализует объекты семафора. Семафор управляет атомарным счётчиком, представляющим количество вызовов
release()минус количество вызововacquire(), плюс начальное значение. Методacquire()при необходимости блокируется до тех пор, пока не сможет вернуть управление, не сделав счётчик отрицательным. Если не задано, value по умолчанию равен 1.Необязательный аргумент задаёт начальное value внутреннего счётчика; по умолчанию он равен
1. Если указанное value меньше 0, возбуждаетсяValueError.Изменено в версии 3.3: изменён с фабричной функции на класс.
- acquire(blocking=True, timeout=None)¶
Захватить семафор.
При вызове без аргументов:
Если внутренний счётчик при входе больше нуля, уменьшить его на единицу и немедленно вернуть
True.Если внутренний счётчик при входе равен нулю, блокироваться до пробуждения вызовом
release(). После пробуждения (и когда счётчик станет больше 0) уменьшить счётчик на 1 и вернутьTrue. Каждый вызовrelease()пробуждает ровно один поток. Порядок пробуждения потоков не гарантируется.
При вызове с blocking, установленным в
False, не блокироваться. Если вызов без аргументов привёл бы к блокировке, немедленно вернутьFalse; в противном случае сделать то же, что и при вызове без аргументов, и вернутьTrue.При вызове с timeout, отличным от
None, блокируется не более чем на timeout секунд. Если acquire не завершится успешно за это время, вернутьFalse. В противном случае вернутьTrue.Изменено в версии 3.2: Добавлен параметр timeout.
Изменено в версии 3.15: Принимает любое вещественное число в качестве 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)
После запуска рабочие потоки вызывают методы acquire и release семафора, когда им нужно подключиться к серверу:
with pool_sema:
conn = connectdb()
try:
# ... use connection ...
finally:
conn.close()
Использование ограниченного семафора снижает вероятность того, что ошибка программирования, приводящая к освобождению семафора чаще, чем захвату, останется незамеченной.
Event objectsОбъекты событий
Это один из простейших механизмов взаимодействия между потоками: один поток сигнализирует о событии, а другие потоки ожидают его.
Объект события управляет внутренним флагом, который можно установить в 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 objectsОбъекты Timer
Этот класс представляет действие, которое должно быть запущено только по прошествии определенного времени – таймер. Timer является подклассом Thread и, таким образом, также служит примером создания пользовательских потоков.
Таймеры запускаются, как и потоки, вызовом их метода Timer.start. Таймер может быть остановлен (до начала его действия) вызовом метода cancel(). Интервал, который таймер будет ждать перед выполнением своего действия, может не совпадать точно с интервалом, указанным пользователем.
Например:
def hello():
print("hello, world")
t = Timer(30.0, hello)
t.start() # через 30 секунд будет напечатано "hello, world"
- class threading.Timer(interval, function, args=None, kwargs=None)¶
Создает таймер, который выполнит function с аргументами args и именованными аргументами kwargs после того, как пройдет interval секунд. Если args равно
None(по умолчанию), будет использован пустой список. Если kwargs равноNone(по умолчанию), будет использован пустой словарь.Изменено в версии 3.3: изменено с фабричной функции на класс.
- cancel()¶
Останавливает таймер и отменяет выполнение его действия. Это сработает только в том случае, если таймер все еще находится в стадии ожидания.
Barrier objectsОбъекты Barrier
Добавлено в версии 3.2.
Этот класс предоставляет простой примитив синхронизации для использования фиксированным числом потоков, которым нужно ждать друг друга. Каждый из потоков пытается пройти барьер вызовом метода wait() и блокируется до тех пор, пока все потоки не выполнят свои вызовы wait(). В этот момент потоки освобождаются одновременно.
Барьер может быть повторно использован любое количество раз для того же числа потоков.
В качестве примера вот простой способ синхронизации потока клиента и сервера:
b = Barrier(2, timeout=5)
def server():
start_server()
b.wait()
while True:
connection = accept_connection()
process_server_connection(connection)
def client():
b.wait()
while True:
connection = make_connection()
process_client_connection(connection)
- class threading.Barrier(parties, action=None, timeout=None)¶
Создает объект барьера для parties потоков. action, если задан, представляет собой вызываемый объект, который будет вызван одним из потоков при их освобождении. timeout – это значение тайм-аута по умолчанию, если не указано для метода
wait().- wait(timeout=None)¶
Пройти барьер. Когда все потоки-участники барьера вызовут эту функцию, они все освобождаются одновременно. Если указан timeout, он используется вместо тайм-аута, переданного конструктору класса.
Возвращаемое значение – целое число в диапазоне от 0 до parties – 1, различное для каждого потока. Это может быть использовано для выбора потока, который выполнит специальную служебную работу, например:
pythoni = barrier.wait() if i == 0: # Только одному потоку нужно напечатать это print("passed the barrier")Если конструктору был передан action, один из потоков вызовет его перед освобождением. Если этот вызов вызовет ошибку, барьер переводится в состояние broken.
Если вызов завершается по тайм-ауту, барьер переводится в состояние broken.
Этот метод может вызвать исключение
BrokenBarrierError, если барьер находится в состоянии broken или был сброшен, пока поток ожидает.
- reset()¶
Возвращает барьер в исходное пустое состояние. Все потоки, ожидающие на нем, получат исключение
BrokenBarrierError.Обратите внимание, что использование этой функции может потребовать некоторой внешней синхронизации, если есть другие потоки, чье состояние неизвестно. Если барьер находится в состоянии broken, возможно, лучше просто оставить его и создать новый.
- abort()¶
Переводит барьер в состояние broken. Это приводит к тому, что любые активные или будущие вызовы
wait()завершаются ошибкой сBrokenBarrierError. Используйте это, например, если один из потоков должен прерваться, чтобы избежать взаимоблокировки приложения.Возможно, предпочтительнее просто создать барьер с разумным значением timeout, чтобы автоматически защититься от выхода одного из потоков из строя.
- parties¶
Количество потоков, необходимое для прохождения барьера.
- n_waiting¶
Количество потоков, ожидающих в барьере в данный момент.
- broken¶
Логическое значение, которое равно
True, если барьер находится в сломанном состоянии.
- exception threading.BrokenBarrierError¶
Это исключение, подкласс
RuntimeError, возникает, когда объектBarrierсбрасывается или ломается.
Использование блокировок, условий и семафоров в операторе with
Все объекты, предоставляемые этим модулем, которые имеют методы acquire и release, могут использоваться как контекстные менеджеры для оператора with.
Метод acquire будет вызван при входе в блок, а release – при выходе из блока. Таким образом,
следующий фрагмент:
with some_lock:
# сделать что-то...
эквивалентно следующему:
some_lock.acquire()
try:
# сделать что-то...
finally:
some_lock.release()
В настоящее время объекты Lock, RLock, Condition,
Semaphore и BoundedSemaphore могут использоваться как контекстные менеджеры
with оператора.