Документация Python неофициальный перевод

threading.md

590 строк · 74.3 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.5/library/threading.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 17.1. [`threading`](https://python-all.ru/3.5/library/threading.html#module-threading) – Параллелизм на основе потоков89**Исходный код:** [Lib/threading.py](https://python-all.ru/src/3.5/Lib/threading.py)1011---1213Этот модуль предоставляет высокоуровневые интерфейсы для работы с потоками поверх низкоуровневого модуля [`_thread`](https://python-all.ru/3.5/library/_thread.html#module-_thread). Смотрите также модуль [`queue`](https://python-all.ru/3.5/library/queue.html#module-queue).1415Модуль [`dummy_threading`](https://python-all.ru/3.5/library/dummy_threading.html#module-dummy_threading) предоставляется для ситуаций, когда [`threading`](https://python-all.ru/3.5/library/threading.html#module-threading) нельзя использовать, поскольку [`_thread`](https://python-all.ru/3.5/library/_thread.html#module-_thread) отсутствует.1617> **Примечание**18>19> Хотя они не перечислены ниже, имена `camelCase`, которые использовались для некоторых методов и функций в этом модуле в серии Python 2.x, по-прежнему поддерживаются этим модулем.2021Этот модуль определяет следующие функции:2223#### `threading.active_count()`2425Возвращает количество [`Thread`](https://python-all.ru/3.5/library/threading.html#threading.Thread) объектов, которые сейчас активны. Возвращённое число равно длине списка, возвращаемого [`enumerate()`](https://python-all.ru/3.5/library/threading.html#threading.enumerate).2627#### `threading.current_thread()`2829Возвращает текущий [`Thread`](https://python-all.ru/3.5/library/threading.html#threading.Thread) объект, соответствующий потоку управления вызывающего кода. Если поток управления вызывающего кода не был создан через модуль [`threading`](https://python-all.ru/3.5/library/threading.html#module-threading), возвращается фиктивный объект потока с ограниченной функциональностью.3031#### `threading.get_ident()`3233Возвращает «идентификатор потока» текущего потока. Это ненулевое целое число. Его значение не имеет прямого смысла; оно предназначено как уникальная метка, используемая, например, для индексации словаря с данными, специфичными для потока. Идентификаторы потоков могут быть переиспользованы, когда один поток завершается, а другой создаётся.3435Новое в версии 3.3.3637#### `threading.enumerate()`3839Возвращает список всех активных на данный момент объектов [`Thread`](https://python-all.ru/3.5/library/threading.html#threading.Thread). Список включает потоки-демоны, фиктивные объекты потоков, созданные [`current_thread()`](https://python-all.ru/3.5/library/threading.html#threading.current_thread), и главный поток. Из него исключены завершённые потоки и потоки, которые ещё не были запущены.4041#### `threading.main_thread()`4243Возвращает основной объект [`Thread`](https://python-all.ru/3.5/library/threading.html#threading.Thread). В обычных условиях главный поток – это поток, с которого был запущен интерпретатор Python.4445Новое в версии 3.4.4647#### `threading.settrace(func)`4849Устанавливает трассировочную функцию для всех потоков, запущенных из модуля [`threading`](https://python-all.ru/3.5/library/threading.html#module-threading). Функция *func* будет передана в [`sys.settrace()`](https://python-all.ru/3.5/library/sys.html#sys.settrace) для каждого потока перед вызовом его метода [`run()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.run).5051#### `threading.setprofile(func)`5253Устанавливает профилирующую функцию для всех потоков, запущенных из модуля [`threading`](https://python-all.ru/3.5/library/threading.html#module-threading). Функция *func* будет передана в [`sys.setprofile()`](https://python-all.ru/3.5/library/sys.html#sys.setprofile) для каждого потока перед вызовом его метода [`run()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.run).5455#### `threading.stack_size([size])`5657Возвращает размер стека потока, используемый при создании новых потоков. Необязательный аргумент *size* задаёт размер стека для последующих создаваемых потоков и должен быть 0 (использовать платформенное или сконфигурированное значение по умолчанию) или положительным целым числом не менее 32 768 (32 КиБ). Если *size* не указан, используется 0. Если изменение размера стека потока не поддерживается, возбуждается исключение [`RuntimeError`](https://python-all.ru/3.5/library/exceptions.html#RuntimeError). Если указан недопустимый размер стека, возбуждается исключение [`ValueError`](https://python-all.ru/3.5/library/exceptions.html#ValueError), а размер стека не изменяется. В настоящее время 32 КиБ – минимальное поддерживаемое значение размера стека, достаточное для обеспечения пространства для самого интерпретатора. Обратите внимание, что некоторые платформы могут накладывать особые ограничения на значения размера стека, например, требовать минимальный размер стека \> 32 КиБ или выделение памяти, кратное размеру страницы системной памяти – для получения дополнительных сведений следует обращаться к документации платформы (обычно страницы имеют размер 4 КиБ; если нет более точной информации, рекомендуется выбирать размер стека, кратный 4096). Доступность: Windows, системы с POSIX-потоками.5859Этот модуль также определяет следующую константу:6061#### `threading.TIMEOUT_MAX`6263Максимально допустимое значение для параметра *timeout* блокирующих функций ([`Lock.acquire()`](https://python-all.ru/3.5/library/threading.html#threading.Lock.acquire), [`RLock.acquire()`](https://python-all.ru/3.5/library/threading.html#threading.RLock.acquire), [`Condition.wait()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.wait) и т.д.). Указание тайм-аута, превышающего это значение, вызовет исключение [`OverflowError`](https://python-all.ru/3.5/library/exceptions.html#OverflowError).6465Новое в версии 3.2.6667Этот модуль определяет ряд классов, которые подробно описаны в разделах ниже.6869Дизайн этого модуля основан на модели потоков Java, хотя и нестрого. Однако если в Java блокировки и условные переменные являются базовым поведением каждого объекта, то в Python это отдельные объекты. Класс [`Thread`](https://python-all.ru/3.5/library/threading.html#threading.Thread) в Python поддерживает подмножество поведения класса Thread из Java; в настоящее время отсутствуют приоритеты, группы потоков, а потоки нельзя уничтожать, останавливать, приостанавливать, возобновлять или прерывать. Статические методы класса Thread из Java, если они реализованы, отображаются на функции уровня модуля.7071Все описанные ниже методы выполняются атомарно.7273## 17.1.1. Локальные данные потока7475Локальные данные потока – это данные, значения которых привязаны к конкретному потоку. Для управления такими данными достаточно создать экземпляр [`local`](https://python-all.ru/3.5/library/threading.html#threading.local) (или подкласса) и сохранять в нём атрибуты.7677```python78mydata = threading.local()79mydata.x = 180```8182Значения экземпляра будут разными для разных потоков.8384#### `class threading.local`8586Класс, представляющий данные, локальные для потока.8788Подробнее и с большим количеством примеров см. строку документации модуля `_threading_local`.8990## 17.1.2. Объекты потоков9192Класс [`Thread`](https://python-all.ru/3.5/library/threading.html#threading.Thread) представляет собой активность, выполняемую в отдельном потоке управления. Есть два способа определить активность: передать вызываемый объект в конструктор или переопределить метод [`run()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.run) в подклассе. Никакие другие методы (кроме конструктора) не должны переопределяться в подклассе. Иными словами, переопределяйте *только* методы `__init__()` и [`run()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.run) этого класса.9394После создания объекта потока его активность должна быть запущена вызовом метода [`start()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.start) этого потока. Это вызывает метод [`run()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.run) в отдельном потоке управления.9596После запуска активности поток считается «живым». Он перестаёт быть живым, когда завершается его метод [`run()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.run) – либо нормально, либо из-за необработанного исключения. Метод [`is_alive()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.is_alive) проверяет, жив ли поток.9798Другие потоки могут вызывать метод [`join()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.join) потока. Это блокирует вызывающий поток до тех пор, пока поток, чей метод [`join()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.join) вызывается, не завершится.99100У потока есть имя. Имя можно передать конструктору, а также прочитать или изменить через атрибут [`name`](https://python-all.ru/3.5/library/threading.html#threading.Thread.name).101102Поток может быть помечен как «фоновый поток». Смысл этого флага в том, что вся программа Python завершается, когда остаются только фоновые потоки. Начальное значение наследуется от создающего потока. Флаг можно установить через свойство [`daemon`](https://python-all.ru/3.5/library/threading.html#threading.Thread.daemon) или аргумент конструктора *daemon*.103104> **Примечание**105>106> Фоновые потоки принудительно останавливаются при завершении программы. Их ресурсы (такие как открытые файлы, транзакции базы данных и т.п.) могут быть не освобождены должным образом. Если нужно, чтобы потоки завершались корректно, сделайте их не фоновыми и используйте подходящий механизм уведомлений, например [`Event`](https://python-all.ru/3.5/library/threading.html#threading.Event).107108Существует объект «главный поток»; он соответствует начальному потоку управления в программе Python. Это не фоновый поток.109110Существует вероятность создания «фиктивных объектов потоков» (dummy thread objects). Это объекты потоков, соответствующие «внешним потокам» (alien threads) – потокам управления, запущенным за пределами модуля threading, например напрямую из кода на C. Фиктивные объекты потоков имеют ограниченную функциональность: они всегда считаются активными и демоническими, и их нельзя [`join()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.join). Они никогда не удаляются, так как невозможно определить завершение внешних потоков.111112#### `class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)`113114Этот конструктор всегда следует вызывать с именованными аргументами. Аргументы:115116*group* должен быть `None`; зарезервирован для будущего расширения, когда будет реализован класс `ThreadGroup`.117118*target* – это вызываемый объект, который будет вызван методом [`run()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.run). По умолчанию `None`, то есть ничего не вызывается.119120*name* – это имя потока. По умолчанию создаётся уникальное имя вида «Поток-*N*», где *N* – небольшое десятичное число.121122*args* – кортеж аргументов для вызова целевой функции. По умолчанию равен `()`.123124*kwargs* – это словарь именованных аргументов для вызова target. По умолчанию `{}`.125126Если не `None`, параметр *daemon* явно задаёт, является ли поток демоническим. Если `None` (по умолчанию), свойство демоничности наследуется от текущего потока.127128Если подкласс переопределяет конструктор, он должен обязательно вызвать конструктор базового класса (`Thread.__init__()`) перед любыми другими действиями с потоком.129130Изменено в версии 3.3: Добавлен аргумент *daemon*.131132#### `start()`133134Запускает выполнение потока.135136Этот метод должен вызываться не более одного раза для каждого объекта потока. Он обеспечивает вызов метода [`run()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.run) объекта в отдельном потоке управления.137138Этот метод возбуждает исключение [`RuntimeError`](https://python-all.ru/3.5/library/exceptions.html#RuntimeError), если вызван более одного раза для одного и того же объекта потока.139140#### `run()`141142Метод, представляющий действие потока.143144Этот метод можно переопределить в подклассе. Стандартный метод [`run()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.run) вызывает вызываемый объект, переданный конструктору объекта в качестве аргумента *target* (если он есть), с позиционными и именованными аргументами, взятыми из аргументов *args* и *kwargs* соответственно.145146#### `join(timeout=None)`147148Ожидает завершения потока. Блокирует вызывающий поток до тех пор, пока поток, у которого вызван метод [`join()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.join), не завершится – либо нормально, либо из-за необработанного исключения – или пока не истечёт заданный тайм-аут.149150Если аргумент *timeout* указан и не равен `None`, он должен быть числом с плавающей запятой, задающим тайм-аут для операции в секундах (или долях секунды). Поскольку [`join()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.join) всегда возвращает `None`, после [`join()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.join) необходимо вызвать [`is_alive()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.is_alive), чтобы определить, произошёл ли тайм-аут – если поток всё ещё жив, вызов [`join()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.join) был прерван по тайм-ауту.151152Если аргумент *timeout* отсутствует или равен `None`, операция блокируется до завершения потока.153154Поток можно [`join()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.join) много раз.155156[`join()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.join) возбуждает [`RuntimeError`](https://python-all.ru/3.5/library/exceptions.html#RuntimeError), если предпринимается попытка присоединиться к текущему потоку, так как это привело бы к взаимоблокировке. Также ошибочно [`join()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.join) поток до его запуска, и попытки сделать это возбуждают то же исключение.157158#### `name`159160Строка, используемая только для идентификации. Она не имеет семантического значения. Разным потокам можно задать одно и то же имя. Начальное имя устанавливается конструктором.161162#### `getName()`163164#### `setName()`165166Устаревший API геттера/сеттера для [`name`](https://python-all.ru/3.5/library/threading.html#threading.Thread.name); вместо этого используйте его напрямую как свойство.167168#### `ident`169170‘Идентификатор потока’ этого потока или `None`, если поток не был запущен. Это ненулевое целое число. См. функцию [`_thread.get_ident()`](https://python-all.ru/3.5/library/_thread.html#_thread.get_ident). Идентификаторы потоков могут быть повторно использованы, когда один поток завершается и создаётся другой. Идентификатор доступен даже после завершения потока.171172#### `is_alive()`173174Возвращает, жив ли поток.175176Этот метод возвращает `True` непосредственно перед началом выполнения метода [`run()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.run) и до завершения метода [`run()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.run). Функция модуля [`enumerate()`](https://python-all.ru/3.5/library/threading.html#threading.enumerate) возвращает список всех живых потоков.177178#### `daemon`179180Логическое значение, указывающее, является ли данный поток демоном (True) или нет (False). Это значение должно быть установлено до вызова [`start()`](https://python-all.ru/3.5/library/threading.html#threading.Thread.start), иначе будет вызвано исключение [`RuntimeError`](https://python-all.ru/3.5/library/exceptions.html#RuntimeError). Его начальное значение наследуется от создавшего потока; главный поток не является потоком-демоном, поэтому все потоки, созданные в главном потоке, по умолчанию имеют [`daemon`](https://python-all.ru/3.5/library/threading.html#threading.Thread.daemon) = `False`.181182Вся программа Python завершается, когда не остаётся ни одного живого потока, не являющегося демоном.183184#### `isDaemon()`185186#### `setDaemon()`187188Устаревший API геттера/сеттера для [`daemon`](https://python-all.ru/3.5/library/threading.html#threading.Thread.daemon); вместо этого используйте его напрямую как свойство.189190**Особенность реализации CPython:** В CPython из-за [глобальной блокировки интерпретатора](https://python-all.ru/3.5/glossary.html#term-global-interpreter-lock) только один поток может выполнять код Python одновременно (хотя некоторые библиотеки, ориентированные на производительность, могут обойти это ограничение). Если вы хотите, чтобы ваше приложение лучше использовало вычислительные ресурсы многоядерных машин, рекомендуется использовать [`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing) или [`concurrent.futures.ProcessPoolExecutor`](https://python-all.ru/3.5/library/concurrent.futures.html#concurrent.futures.ProcessPoolExecutor). Однако многопоточность всё ещё является подходящей моделью, если нужно запустить несколько задач с интенсивным вводом-выводом одновременно.191192## 17.1.3. Объекты блокировок193194Примитивная блокировка – это примитив синхронизации, который при захвате не принадлежит какому-либо определённому потоку. В Python в настоящее время это самый низкоуровневый доступный примитив синхронизации, реализованный непосредственно в расширении модуля [`_thread`](https://python-all.ru/3.5/library/_thread.html#module-_thread).195196Примитивная блокировка может находиться в одном из двух состояний: «захвачена» или «свободна». Она создаётся в свободном состоянии. У неё есть два основных метода: [`acquire()`](https://python-all.ru/3.5/library/threading.html#threading.Lock.acquire) и [`release()`](https://python-all.ru/3.5/library/threading.html#threading.Lock.release). Когда состояние свободно, вызов [`acquire()`](https://python-all.ru/3.5/library/threading.html#threading.Lock.acquire) переводит его в захваченное и немедленно возвращается. Когда состояние захвачено, [`acquire()`](https://python-all.ru/3.5/library/threading.html#threading.Lock.acquire) блокируется до тех пор, пока вызов [`release()`](https://python-all.ru/3.5/library/threading.html#threading.Lock.release) в другом потоке не переведёт его в свободное; затем вызов [`acquire()`](https://python-all.ru/3.5/library/threading.html#threading.Lock.acquire) снова устанавливает захваченное состояние и возвращается. Метод [`release()`](https://python-all.ru/3.5/library/threading.html#threading.Lock.release) следует вызывать только в захваченном состоянии; он переводит состояние в свободное и немедленно возвращается. При попытке освободить свободную блокировку будет возбуждено [`RuntimeError`](https://python-all.ru/3.5/library/exceptions.html#RuntimeError).197198Блокировки также поддерживают [протокол контекстного менеджера](https://python-all.ru/3.5/library/threading.html#with-locks).199200Когда несколько потоков заблокированы в [`acquire()`](https://python-all.ru/3.5/library/threading.html#threading.Lock.acquire) в ожидании перехода состояния в свободное, только один поток продолжает выполнение, когда вызов [`release()`](https://python-all.ru/3.5/library/threading.html#threading.Lock.release) переводит состояние в свободное; какой именно из ожидающих потоков продолжит, не определено и может различаться в разных реализациях.201202Все методы выполняются атомарно.203204#### `class threading.Lock`205206Класс, реализующий объекты примитивной блокировки. Как только поток захватил блокировку, последующие попытки захватить её блокируются до тех пор, пока она не будет освобождена; любой поток может освободить её.207208Отметим, что `Lock` – это фабричная функция, возвращающая экземпляр наиболее эффективной версии конкретного класса блокировки, поддерживаемой платформой.209210#### `acquire(blocking=True, timeout=-1)`211212Захватывает блокировку, блокирующую или неблокирующую.213214При вызове с аргументом *blocking*, установленным в `True` (по умолчанию), блокируется до тех пор, пока блокировка не станет свободной, затем устанавливает её в захваченное состояние и возвращает `True`.215216При вызове с аргументом *blocking*, установленным в `False`, не блокируется. Если вызов с *blocking*, установленным в `True`, должен был бы заблокироваться, немедленно возвращает `False`; в противном случае устанавливает блокировку в захваченное состояние и возвращает `True`.217218Если при вызове аргументу *timeout* с плавающей запятой задано положительное значение, блокировка происходит на время не более указанного *timeout* секунд, пока не будет получена блокировка. Значение *timeout*, равное `-1`, означает бесконечное ожидание. Запрещено указывать *timeout*, когда *blocking* равно false.219220Возвращаемое значение равно `True`, если блокировка успешно захвачена, и `False` в противном случае (например, если *timeout* истёк).221222Изменено в версии 3.2: Параметр *timeout* является новым.223224Изменено в версии 3.2: Захват блокировки теперь может прерываться сигналами на POSIX.225226#### `release()`227228Освобождает блокировку. Может вызываться из любого потока, а не только из того, который захватил блокировку.229230Когда блокировка установлена, сбрасывает её в снятое состояние и возвращает управление. Если другие потоки заблокированы в ожидании освобождения блокировки, ровно одному из них разрешается продолжить работу.231232При вызове на не заблокированной блокировке возбуждается исключение [`RuntimeError`](https://python-all.ru/3.5/library/exceptions.html#RuntimeError).233234Возвращаемое значение отсутствует.235236## 17.1.4. Объекты RLock237238Повторно входимая блокировка – это примитив синхронизации, который может быть захвачен одним и тем же потоком несколько раз. Внутри она использует понятия «поток-владелец» и «уровень рекурсии» в дополнение к состоянию «заблокировано/разблокировано», используемому простыми блокировками. В заблокированном состоянии блокировкой владеет какой-то поток; в разблокированном состоянии ею не владеет ни один поток.239240Чтобы захватить блокировку, поток вызывает её метод [`acquire()`](https://python-all.ru/3.5/library/threading.html#threading.RLock.acquire); этот метод возвращает управление, когда поток получает блокировку. Чтобы освободить блокировку, поток вызывает её метод [`release()`](https://python-all.ru/3.5/library/threading.html#threading.Lock.release). Пары вызовов [`acquire()`](https://python-all.ru/3.5/library/threading.html#threading.Lock.acquire)/[`release()`](https://python-all.ru/3.5/library/threading.html#threading.Lock.release) могут быть вложенными; только последний [`release()`](https://python-all.ru/3.5/library/threading.html#threading.Lock.release) ( [`release()`](https://python-all.ru/3.5/library/threading.html#threading.Lock.release) самой внешней пары) переводит блокировку в состояние «свободна» и позволяет другому потоку, заблокированному в [`acquire()`](https://python-all.ru/3.5/library/threading.html#threading.Lock.acquire), продолжить работу.241242Рекурсивные блокировки также поддерживают [протокол контекстного менеджера](https://python-all.ru/3.5/library/threading.html#with-locks).243244#### `class threading.RLock`245246Этот класс реализует объекты повторно входимой блокировки. Повторно входимая блокировка должна быть освобождена тем же потоком, который её захватил. После того как поток захватил повторно входимую блокировку, этот же поток может захватить её снова без блокировки; при этом поток должен освободить её один раз за каждый захват.247248Обратите внимание, что `RLock` на самом деле является фабричной функцией, которая возвращает экземпляр наиболее эффективной версии конкретного класса RLock, поддерживаемой платформой.249250#### `acquire(blocking=True, timeout=-1)`251252Захватывает блокировку, блокирующую или неблокирующую.253254При вызове без аргументов: если этот поток уже владеет блокировкой, увеличить уровень рекурсии на единицу и немедленно вернуться. В противном случае, если блокировкой владеет другой поток, блокироваться до тех пор, пока блокировка не будет освобождена. Как только блокировка освобождена (не принадлежит ни одному потоку), захватить владение, установить уровень рекурсии в единицу и вернуться. Если несколько потоков заблокированы в ожидании освобождения блокировки, только один из них сможет захватить владение. В этом случае возвращаемого значения нет.255256При вызове с аргументом *blocking*, установленным в true, делает то же самое, что и при вызове без аргументов, и возвращает true.257258При вызове с аргументом *blocking*, установленным в false, не блокируется. Если вызов без аргументов привёл бы к блокировке, немедленно возвращает false; в противном случае делает то же самое, что и при вызове без аргументов, и возвращает true.259260При вызове с аргументом *timeout* типа float, установленным в положительное значение, блокируется не более чем на число секунд, указанное в *timeout*, и до тех пор, пока блокировка не будет получена. Возвращает True, если блокировка получена, и False, если истекло время ожидания.261262Изменено в версии 3.2: Параметр *timeout* является новым.263264#### `release()`265266Освобождает блокировку, уменьшая уровень рекурсии. Если после уменьшения он становится нулевым, сбрасывает блокировку в разблокированное состояние (не принадлежит ни одному потоку), и, если другие потоки заблокированы в ожидании освобождения блокировки, ровно одному из них разрешается продолжить. Если после уменьшения уровень рекурсии всё ещё ненулевой, блокировка остаётся заблокированной и принадлежит вызывающему потоку.267268Вызывайте этот метод только тогда, когда вызывающий поток владеет блокировкой. Возбуждается исключение [`RuntimeError`](https://python-all.ru/3.5/library/exceptions.html#RuntimeError), если этот метод вызван, когда блокировка не захвачена.269270Возвращаемое значение отсутствует.271272## 17.1.5. Объекты условий273274Переменная условия всегда связана с какой-либо блокировкой; её можно передать, или же она будет создана по умолчанию. Передача блокировки полезна, когда несколько переменных условия должны совместно использовать одну и ту же блокировку. Блокировка является частью объекта условия: не нужно отслеживать её отдельно.275276Переменная условия подчиняется [протоколу менеджера контекста](https://python-all.ru/3.5/library/threading.html#with-locks): использование выражения `with` захватывает связанную блокировку на время выполнения вложенного блока. Методы [`acquire()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.acquire) и [`release()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.release) также вызывают соответствующие методы связанной блокировки.277278Остальные методы должны вызываться, когда связанная блокировка удерживается. Метод [`wait()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.wait) освобождает блокировку, а затем блокируется, пока другой поток не пробудит его вызовом [`notify()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.notify) или [`notify_all()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.notify_all). После пробуждения метод [`wait()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.wait) повторно захватывает блокировку и возвращает управление. Также можно указать тайм-аут.279280Метод [`notify()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.notify) пробуждает один из потоков, ожидающих переменную условия, если таковые имеются. Метод [`notify_all()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.notify_all) пробуждает все потоки, ожидающие переменную условия.281282Примечание: методы [`notify()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.notify) и [`notify_all()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.notify_all) не освобождают блокировку; это означает, что пробуждённый поток (или потоки) не вернутся из своего вызова [`wait()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.wait) немедленно, а только когда поток, вызвавший [`notify()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.notify) или [`notify_all()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.notify_all), наконец откажется от владения блокировкой.283284Типичный стиль программирования с использованием переменных условия использует блокировку для синхронизации доступа к некоторому общему состоянию; потоки, заинтересованные в определённом изменении состояния, многократно вызывают [`wait()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.wait), пока не увидят желаемое состояние, в то время как потоки, изменяющие состояние, вызывают [`notify()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.notify) или [`notify_all()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.notify_all), когда они изменяют состояние таким образом, что это может быть желаемым состоянием для одного из ожидающих. Например, следующий код представляет собой общую ситуацию «производитель-потребитель» с неограниченной ёмкостью буфера:285286```python287# Потребить один элемент288with cv:289    while not an_item_is_available():290        cv.wait()291    get_an_available_item()292293# Произвести один элемент294with cv:295    make_an_item_available()296    cv.notify()297```298299Цикл `while`, проверяющий условие приложения, необходим, потому что [`wait()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.wait) может вернуться после произвольно долгого времени, и условие, которое вызвало вызов [`notify()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.notify), может больше не выполняться. Это присуще многопоточному программированию. Метод [`wait_for()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.wait_for) можно использовать для автоматизации проверки условия и упрощения расчёта тайм-аутов:300301```python302# Потребить элемент303with cv:304    cv.wait_for(an_item_is_available)305    get_an_available_item()306```307308Чтобы выбрать между [`notify()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.notify) и [`notify_all()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.notify_all), подумайте, может ли одно изменение состояния быть интересным только одному или нескольким ожидающим потокам. Например, в типичной ситуации «производитель-потребитель» добавление одного элемента в буфер требует пробуждения только одного потока-потребителя.309310#### `class threading.Condition(lock=None)`311312Этот класс реализует объекты переменной условия. Переменная условия позволяет одному или нескольким потокам ожидать, пока другой поток не уведомит их.313314Если аргумент *lock* передан и не равен `None`, он должен быть объектом [`Lock`](https://python-all.ru/3.5/library/threading.html#threading.Lock) или [`RLock`](https://python-all.ru/3.5/library/threading.html#threading.RLock) и используется в качестве базовой блокировки. В противном случае создаётся новый объект [`RLock`](https://python-all.ru/3.5/library/threading.html#threading.RLock), который используется в качестве базовой блокировки.315316Изменено в версии 3.3: изменён с фабричной функции на класс.317318#### `acquire(*args)`319320Захватывает базовую блокировку. Этот метод вызывает соответствующий метод базовой блокировки; возвращаемое значение – то, что возвращает этот метод.321322#### `release()`323324Освобождает базовую блокировку. Этот метод вызывает соответствующий метод базовой блокировки; возвращаемое значение отсутствует.325326#### `wait(timeout=None)`327328Ожидает до получения уведомления или до наступления тайм-аута. Если вызывающий поток не захватил блокировку при вызове этого метода, возбуждается [`RuntimeError`](https://python-all.ru/3.5/library/exceptions.html#RuntimeError).329330Этот метод освобождает базовую блокировку, а затем блокируется до тех пор, пока не будет пробуждён вызовом [`notify()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.notify) или [`notify_all()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.notify_all) для той же переменной условия в другом потоке, или пока не наступит необязательный тайм-аут. После пробуждения или истечения тайм-аута он повторно захватывает блокировку и возвращает управление.331332Если аргумент *timeout* присутствует и не равен `None`, он должен быть числом с плавающей точкой, задающим время ожидания операции в секундах (или его долях).333334Если базовая блокировка является [`RLock`](https://python-all.ru/3.5/library/threading.html#threading.RLock), она не освобождается с помощью метода [`release()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.release), поскольку это может не разблокировать блокировку, если она была захвачена несколько раз рекурсивно. Вместо этого используется внутренний интерфейс класса [`RLock`](https://python-all.ru/3.5/library/threading.html#threading.RLock), который действительно разблокирует её, даже если она была рекурсивно захвачена несколько раз. Затем используется другой внутренний интерфейс для восстановления уровня рекурсии при повторном захвате блокировки.335336Возвращаемое значение равно `True`, если только заданный *timeout* не истёк; в этом случае оно равно `False`.337338Изменено в версии 3.2: Ранее метод всегда возвращал `None`.339340#### `wait_for(predicate, timeout=None)`341342Ожидает, пока условие не станет истинным. *predicate* должен быть вызываемым объектом, результат которого будет интерпретироваться как булево значение. Можно указать *timeout*, задающий максимальное время ожидания.343344Этот вспомогательный метод может многократно вызывать [`wait()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.wait) до тех пор, пока предикат не будет удовлетворён или пока не наступит тайм-аут. Возвращаемое значение – последнее возвращаемое значение предиката; оно будет равно `False`, если время ожидания истекло.345346Если не учитывать возможность тайм-аута, вызов этого метода примерно эквивалентен следующему коду:347348```python349while not predicate():350    cv.wait()351```352353Следовательно, применяются те же правила, что и для [`wait()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.wait): блокировка должна быть захвачена при вызове и повторно захватывается при возврате. Предикат вычисляется при удерживаемой блокировке.354355Новое в версии 3.2.356357#### `notify(n=1)`358359По умолчанию пробуждает один поток, ожидающий на этом условии, если таковой имеется. Если вызывающий поток не захватил блокировку на момент вызова этого метода, возникает [`RuntimeError`](https://python-all.ru/3.5/library/exceptions.html#RuntimeError).360361Этот метод пробуждает не более *n* потоков, ожидающих на переменной условия; если ни один поток не ожидает, он ничего не делает.362363Текущая реализация пробуждает ровно *n* потоков, если ожидает не менее *n* потоков. Однако полагаться на такое поведение небезопасно. В будущем оптимизированная реализация может иногда пробуждать более *n* потоков.364365Примечание: пробуждённый поток на самом деле не возвращается из вызова [`wait()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.wait), пока не сможет повторно захватить блокировку. Поскольку [`notify()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.notify) не освобождает блокировку, это должен сделать его вызывающий код.366367#### `notify_all()`368369Пробуждает все потоки, ожидающие на этом условии. Этот метод действует как [`notify()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.notify), но пробуждает все ожидающие потоки вместо одного. Если вызывающий поток не захватил блокировку на момент вызова этого метода, возникает [`RuntimeError`](https://python-all.ru/3.5/library/exceptions.html#RuntimeError).370371## 17.1.6. Объекты семафоров372373Это один из старейших примитивов синхронизации в истории компьютерных наук, изобретённый ранним нидерландским учёным Эдсгером В. Дейкстрой (он использовал имена `P()` и `V()` вместо [`acquire()`](https://python-all.ru/3.5/library/threading.html#threading.Semaphore.acquire) и [`release()`](https://python-all.ru/3.5/library/threading.html#threading.Semaphore.release)).374375Семафор управляет внутренним счётчиком, который уменьшается при каждом вызове [`acquire()`](https://python-all.ru/3.5/library/threading.html#threading.Semaphore.acquire) и увеличивается при каждом вызове [`release()`](https://python-all.ru/3.5/library/threading.html#threading.Semaphore.release). Счётчик никогда не может стать меньше нуля; когда [`acquire()`](https://python-all.ru/3.5/library/threading.html#threading.Semaphore.acquire) обнаруживает, что он равен нулю, он блокируется, ожидая, пока другой поток вызовет [`release()`](https://python-all.ru/3.5/library/threading.html#threading.Semaphore.release).376377Семафоры также поддерживают [протокол менеджера контекста](https://python-all.ru/3.5/library/threading.html#with-locks).378379#### `class threading.Semaphore(value=1)`380381Этот класс реализует объекты семафоров. Семафор управляет счётчиком, представляющим число вызовов [`release()`](https://python-all.ru/3.5/library/threading.html#threading.Semaphore.release) минус число вызовов [`acquire()`](https://python-all.ru/3.5/library/threading.html#threading.Semaphore.acquire), плюс начальное значение. Метод [`acquire()`](https://python-all.ru/3.5/library/threading.html#threading.Semaphore.acquire) блокируется при необходимости, пока не сможет вернуться, не сделав счётчик отрицательным. Если не задано, *value* по умолчанию равно 1.382383Необязательный аргумент задаёт начальное *value* для внутреннего счётчика; по умолчанию оно равно `1`. Если указанное *value* меньше 0, возникает [`ValueError`](https://python-all.ru/3.5/library/exceptions.html#ValueError).384385Изменено в версии 3.3: изменён с фабричной функции на класс.386387#### `acquire(blocking=True, timeout=None)`388389Захватывает семафор.390391При вызове без аргументов: если внутренний счётчик больше нуля на входе, уменьшить его на единицу и вернуться немедленно. Если он равен нулю на входе, блокироваться, ожидая, пока какой-либо другой поток не вызовет [`release()`](https://python-all.ru/3.5/library/threading.html#threading.Semaphore.release), чтобы сделать его больше нуля. Это делается с правильной взаимоблокировкой, так что если несколько вызовов [`acquire()`](https://python-all.ru/3.5/library/threading.html#threading.Semaphore.acquire) заблокированы, [`release()`](https://python-all.ru/3.5/library/threading.html#threading.Semaphore.release) разбудит ровно один из них. Реализация может выбрать один из них случайным образом, поэтому порядок, в котором блокированные потоки пробуждаются, не должен использоваться для принятия решений. Возвращает true (или блокируется на неопределённый срок).392393При вызове с *blocking*, установленным в false, не блокироваться. Если вызов без аргумента заблокировался бы, немедленно вернуть false; в противном случае сделать то же самое, что и при вызове без аргументов, и вернуть true.394395При вызове с *timeout*, отличным от `None`, будет блокироваться не более *timeout* секунд. Если acquire не завершится успешно за это время, возвращает false. В противном случае возвращает true.396397Изменено в версии 3.2: Параметр *timeout* является новым.398399#### `release()`400401Освобождает семафор, увеличивая внутренний счётчик на единицу. Когда он был равен нулю при входе и другой поток ожидает, когда он снова станет больше нуля, пробуждает этот поток.402403#### `class threading.BoundedSemaphore(value=1)`404405Класс, реализующий объекты ограниченного семафора. Ограниченный семафор проверяет, что его текущее значение не превышает начальное. Если превышает, возникает [`ValueError`](https://python-all.ru/3.5/library/exceptions.html#ValueError). В большинстве ситуаций семафоры используются для защиты ресурсов с ограниченной ёмкостью. Если семафор освобождается слишком много раз, это признак ошибки. Если не указано, *value* по умолчанию равно 1.406407Изменено в версии 3.3: изменён с фабричной функции на класс.408409### 17.1.6.1. [`Semaphore`](https://python-all.ru/3.5/library/threading.html#threading.Semaphore) Пример410411Семафоры часто используются для защиты ресурсов с ограниченной ёмкостью, например, сервера базы данных. В любой ситуации, когда размер ресурса фиксирован, следует использовать ограниченный семафор. Перед запуском рабочих потоков главный поток инициализирует семафор:412413```python414maxconnections = 5415# ...416pool_sema = BoundedSemaphore(value=maxconnections)417```418419После запуска рабочие потоки вызывают методы acquire и release семафора, когда им нужно подключиться к серверу:420421```python422with pool_sema:423    conn = connectdb()424    try:425        # ... использовать соединение ...426    finally:427        conn.close()428```429430Использование ограниченного семафора снижает вероятность того, что ошибка программирования, приводящая к освобождению семафора большее количество раз, чем его захват, останется незамеченной.431432## 17.1.7. Объекты событий433434Это один из простейших механизмов взаимодействия между потоками: один поток сигнализирует о событии, а другие потоки ожидают его.435436Объект события управляет внутренним флагом, который можно установить в true с помощью метода [`set()`](https://python-all.ru/3.5/library/threading.html#threading.Event.set) и сбросить в false с помощью метода [`clear()`](https://python-all.ru/3.5/library/threading.html#threading.Event.clear). Метод [`wait()`](https://python-all.ru/3.5/library/threading.html#threading.Event.wait) блокируется до тех пор, пока флаг не станет true.437438#### `class threading.Event`439440Класс, реализующий объекты событий. Событие управляет флагом, который можно установить в true методом [`set()`](https://python-all.ru/3.5/library/threading.html#threading.Event.set) и сбросить в false методом [`clear()`](https://python-all.ru/3.5/library/threading.html#threading.Event.clear). Метод [`wait()`](https://python-all.ru/3.5/library/threading.html#threading.Event.wait) блокируется до тех пор, пока флаг не станет true. Изначально флаг равен false.441442Изменено в версии 3.3: изменён с фабричной функции на класс.443444#### `is_set()`445446Возвращает true тогда и только тогда, когда внутренний флаг равен true.447448#### `set()`449450Устанавливает внутренний флаг в true. Все потоки, ожидающие его установки, пробуждаются. Потоки, вызывающие [`wait()`](https://python-all.ru/3.5/library/threading.html#threading.Event.wait) после того, как флаг стал true, не будут блокироваться вовсе.451452#### `clear()`453454Сбрасывает внутренний флаг в false. После этого потоки, вызывающие [`wait()`](https://python-all.ru/3.5/library/threading.html#threading.Event.wait), будут блокироваться до тех пор, пока не будет вызван [`set()`](https://python-all.ru/3.5/library/threading.html#threading.Event.set), чтобы снова установить внутренний флаг в true.455456#### `wait(timeout=None)`457458Блокирует выполнение до тех пор, пока внутренний флаг не станет истинным. Если при входе внутренний флаг уже истинен, немедленно возвращает управление. В противном случае блокируется, пока другой поток не вызовет [`set()`](https://python-all.ru/3.5/library/threading.html#threading.Event.set) для установки флага в истинное значение, или пока не истечёт указанный период ожидания.459460Когда аргумент timeout присутствует и не равен `None`, он должен быть числом с плавающей запятой, задающим время ожидания операции в секундах (или их долях).461462Этот метод возвращает true тогда и только тогда, когда внутренний флаг был установлен в true – до вызова wait или после его начала, поэтому он всегда возвращает `True`, за исключением случая, когда задан тайм-аут и истекает время ожидания.463464Изменено в версии 3.1: Ранее метод всегда возвращал `None`.465466## 17.1.8. Объекты таймеров467468Этот класс представляет действие, которое должно быть выполнено только по прошествии определённого времени – таймер. [`Timer`](https://python-all.ru/3.5/library/threading.html#threading.Timer) является подклассом [`Thread`](https://python-all.ru/3.5/library/threading.html#threading.Thread) и, таким образом, служит примером создания пользовательских потоков.469470Таймеры запускаются, как и потоки, вызовом метода `start()`. Таймер можно остановить (до начала его действия) вызовом метода [`cancel()`](https://python-all.ru/3.5/library/threading.html#threading.Timer.cancel). Интервал, который таймер будет ждать перед выполнением действия, может не совпадать в точности с интервалом, указанным пользователем.471472Например:473474```python475def hello():476    print("hello, world")477478t = Timer(30.0, hello)479t.start()  # через 30 секунд будет выведено "hello, world"480```481482#### `class threading.Timer(interval, function, args=None, kwargs=None)`483484Создаёт таймер, который запустит *function* с аргументами *args* и именованными аргументами *kwargs* по прошествии *interval* секунд. Если *args* равен `None` (значение по умолчанию), будет использован пустой список. Если *kwargs* равен `None` (значение по умолчанию), будет использован пустой словарь.485486Изменено в версии 3.3: изменён с фабричной функции на класс.487488#### `cancel()`489490Останавливает таймер и отменяет выполнение его действия. Это сработает, только если таймер всё ещё находится в стадии ожидания.491492## 17.1.9. Объекты барьеров493494Новое в версии 3.2.495496Этот класс предоставляет простой примитив синхронизации для использования фиксированным числом потоков, которым нужно ожидать друг друга. Каждый из потоков пытается пройти барьер, вызывая метод [`wait()`](https://python-all.ru/3.5/library/threading.html#threading.Barrier.wait), и блокируется до тех пор, пока все потоки не произведут вызов. В этот момент все потоки освобождаются одновременно.497498Барьер можно использовать повторно любое количество раз для того же числа потоков.499500В качестве примера приведён простой способ синхронизации потоков клиента и сервера:501502```python503b = Barrier(2, timeout=5)504505def server():506    start_server()507    b.wait()508    while True:509        connection = accept_connection()510        process_server_connection(connection)511512def client():513    b.wait()514    while True:515        connection = make_connection()516        process_client_connection(connection)517```518519#### `class threading.Barrier(parties, action=None, timeout=None)`520521Создаёт объект барьера для *parties* потоков. *action*, если задан, – это вызываемый объект, который будет вызван одним из потоков при освобождении. *timeout* – значение тайм-аута по умолчанию, если не указано другое для метода [`wait()`](https://python-all.ru/3.5/library/threading.html#threading.Barrier.wait).522523#### `wait(timeout=None)`524525Пройти барьер. Когда все потоки-участники барьера вызовут эту функцию, они все освобождаются одновременно. Если указан *timeout*, он используется с приоритетом перед любым значением, переданным конструктору класса.526527Возвращаемое значение – целое число в диапазоне от 0 до *parties* – 1, различное для каждого потока. Это можно использовать для выбора потока, который выполнит специальные вспомогательные действия, например:528529```python530i = barrier.wait()531if i == 0:532    # Выводить это должен только один поток533    print("passed the barrier")534```535536Если конструктору был передан *action*, один из потоков вызовет его перед освобождением. Если этот вызов вызовет ошибку, барьер переводится в состояние сбоя.537538Если вызов истекает по таймауту, барьер переводится в сломанное состояние.539540Этот метод может вызвать исключение [`BrokenBarrierError`](https://python-all.ru/3.5/library/threading.html#threading.BrokenBarrierError), если барьер сломан или сброшен, пока поток ожидает.541542#### `reset()`543544Возвращает барьер в исходное пустое состояние. Любые потоки, ожидающие его, получат исключение [`BrokenBarrierError`](https://python-all.ru/3.5/library/threading.html#threading.BrokenBarrierError).545546Обратите внимание, что использование этой функции может потребовать внешней синхронизации, если есть другие потоки, состояние которых неизвестно. Если барьер сломан, возможно, лучше просто оставить его и создать новый.547548#### `abort()`549550Переводит барьер в сломанное состояние. Это приводит к тому, что все активные или будущие вызовы [`wait()`](https://python-all.ru/3.5/library/threading.html#threading.Barrier.wait) завершаются с ошибкой [`BrokenBarrierError`](https://python-all.ru/3.5/library/threading.html#threading.BrokenBarrierError). Используйте это, например, если один из потоков необходимо прервать, чтобы избежать взаимоблокировки приложения.551552Возможно, предпочтительнее просто создать барьер с разумным значением *timeout*, чтобы автоматически защититься от сбоя одного из потоков.553554#### `parties`555556Количество потоков, необходимое для прохождения барьера.557558#### `n_waiting`559560Количество потоков, ожидающих в данный момент на барьере.561562#### `broken`563564Логическое значение, которое равно `True`, если барьер находится в сломанном состоянии.565566#### `exception threading.BrokenBarrierError`567568Это исключение, подкласс [`RuntimeError`](https://python-all.ru/3.5/library/exceptions.html#RuntimeError), вызывается, когда объект [`Barrier`](https://python-all.ru/3.5/library/threading.html#threading.Barrier) сбрасывается или ломается.569570## 17.1.10. Использование блокировок, условий и семафоров в операторе [`with`](https://python-all.ru/3.5/reference/compound_stmts.html#with)571572Все объекты, предоставляемые этим модулем, которые имеют методы `acquire()` и `release()`, могут использоваться как контекстные менеджеры для оператора [`with`](https://python-all.ru/3.5/reference/compound_stmts.html#with). Метод `acquire()` будет вызываться при входе в блок, а `release()` – при выходе из блока. Поэтому следующий фрагмент:573574```python575with some_lock:576    # выполнить какие-то действия...577```578579эквивалентно:580581```python582some_lock.acquire()583try:584    # выполнить какие-то действия...585finally:586    some_lock.release()587```588589В настоящее время объекты [`Lock`](https://python-all.ru/3.5/library/threading.html#threading.Lock), [`RLock`](https://python-all.ru/3.5/library/threading.html#threading.RLock), [`Condition`](https://python-all.ru/3.5/library/threading.html#threading.Condition), [`Semaphore`](https://python-all.ru/3.5/library/threading.html#threading.Semaphore) и [`BoundedSemaphore`](https://python-all.ru/3.5/library/threading.html#threading.BoundedSemaphore) могут использоваться как контекстные менеджеры оператора [`with`](https://python-all.ru/3.5/reference/compound_stmts.html#with).590