threading.md
1> **Источник:** https://python-all.ru/3.1/library/threading.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 16.2. `threading` – Параллелизм на основе потоков89Этот модуль предоставляет интерфейсы threading более высокого уровня поверх низкоуровневого модуля [`_thread`](https://python-all.ru/3.1/library/_thread.html#module-_thread). Смотрите также модуль [`queue`](https://python-all.ru/3.1/library/queue.html#module-queue).1011Модуль [`dummy_threading`](https://python-all.ru/3.1/library/dummy_threading.html#module-dummy_threading) предназначен для ситуаций, когда `threading` не может быть использован из-за отсутствия [`_thread`](https://python-all.ru/3.1/library/_thread.html#module-_thread).1213> **Примечание**14>15> Хотя они не перечислены ниже, имена в стиле `camelCase`, использовавшиеся для некоторых методов и функций в этом модуле в серии Python 2.x, по-прежнему поддерживаются этим модулем.1617**Особенность реализации CPython:** Из-за [*глобальной блокировки интерпретатора (Global Interpreter Lock, GIL)*](https://python-all.ru/3.1/glossary.html#term-global-interpreter-lock) в CPython только один поток может одновременно выполнять Python-код (хотя некоторые библиотеки, ориентированные на производительность, могут обойти это ограничение). Для более эффективного использования вычислительных ресурсов многоядерных машин рекомендуется применять [`multiprocessing`](https://python-all.ru/3.1/library/multiprocessing.html#module-multiprocessing). Однако threading по-прежнему является подходящей моделью для одновременного выполнения нескольких задач, связанных с вводом-выводом.1819Этот модуль определяет следующие функции и объекты:2021#### `threading.active_count()`2223Возвращает количество объектов2425[`Thread`](https://python-all.ru/3.1/library/threading.html#threading.Thread)2627, которые в данный момент активны. Возвращаемое количество равно длине списка, возвращаемого2829[`enumerate()`](https://python-all.ru/3.1/library/threading.html#threading.enumerate)3031.3233#### `threading.Condition()`3435Фабричная функция, возвращающая новый объект условной переменной. Условная переменная позволяет одному или нескольким потокам ожидать, пока другой поток не уведомит их.3637См. [*Объекты Condition*](https://python-all.ru/3.1/library/threading.html#condition-objects).3839#### `threading.current_thread()`4041Возвращает текущий объект4243[`потока`](https://python-all.ru/3.1/library/threading.html#threading.Thread)4445, соответствующий потоку управления вызывающего кода. Если поток управления вызывающего кода не был создан через модуль4647`threading`4849, возвращается фиктивный объект потока с ограниченной функциональностью.5051#### `threading.enumerate()`5253Возвращает список всех объектов5455[`Thread`](https://python-all.ru/3.1/library/threading.html#threading.Thread)5657, которые в данный момент активны. Список включает потоки-демоны, фиктивные объекты потоков, созданные5859[`current_thread()`](https://python-all.ru/3.1/library/threading.html#threading.current_thread)6061, и главный поток. Он исключает завершённые потоки и потоки, которые ещё не были запущены.6263#### `threading.Event()`6465Фабричная функция, возвращающая новый объект события. Событие управляет флагом, который можно установить в true с помощью метода [`set()`](https://python-all.ru/3.1/library/threading.html#threading.Event.set) и сбросить в false с помощью метода `clear()`. Метод `wait()` блокируется, пока флаг не станет true.6667См. [*Объекты Event*](https://python-all.ru/3.1/library/threading.html#event-objects).6869#### `class threading.local`7071Класс, представляющий локальные данные потока. Локальные данные потока – это данные, значения которых специфичны для потока. Чтобы управлять локальными данными потока, достаточно создать экземпляр [`local`](https://python-all.ru/3.1/library/threading.html#threading.local) (или подкласс) и сохранять атрибуты в нем:7273```python74mydata = threading.local()75mydata.x = 176```7778Значения экземпляра будут разными для разных потоков.7980Для получения более подробной информации и многочисленных примеров обратитесь к строке документации модуля `_threading_local`.8182#### `threading.Lock()`8384Фабричная функция, возвращающая новый объект примитивной блокировки. Как только поток захватил блокировку, последующие попытки захватить её блокируются, пока она не будет освобождена; любой поток может освободить её.8586См. [*Объекты блокировки*](https://python-all.ru/3.1/library/threading.html#lock-objects).8788#### `threading.RLock()`8990Фабричная функция, возвращающая новый объект реентерабельной блокировки. Реентерабельная блокировка должна быть освобождена тем потоком, который её захватил. После того как поток захватил реентерабельную блокировку, тот же поток может захватить её снова без блокировки; поток должен освободить её по одному разу за каждое захватывание.9192См. [*Объекты RLock*](https://python-all.ru/3.1/library/threading.html#rlock-objects).9394#### `threading.Semaphore(value=1)`9596Фабричная функция, возвращающая новый объект семафора. Семафор управляет счётчиком, представляющим количество вызовов `release()` минус количество вызовов `acquire()` плюс начальное значение. Метод `acquire()` блокируется при необходимости, пока не сможет вернуться, не сделав счётчик отрицательным. Если не указан, *value* по умолчанию равен 1.9798См. [*Объекты Semaphore*](https://python-all.ru/3.1/library/threading.html#semaphore-objects).99100#### `threading.BoundedSemaphore(value=1)`101102Фабричная функция, возвращающая новый объект ограниченного семафора. Ограниченный семафор проверяет, что его текущее значение не превышает начальное. Если это происходит, возбуждается103104[`ValueError`](https://python-all.ru/3.1/library/exceptions.html#ValueError)105106. В большинстве случаев семафоры используются для защиты ресурсов с ограниченной ёмкостью. Если семафор освобождается слишком много раз, это признак ошибки. Если не указан,107108*value*109110по умолчанию равен 1.111112#### `class threading.Thread`113114Класс, представляющий поток управления. Этот класс можно безопасно наследовать ограниченным образом.115116См. [*Объекты потока*](https://python-all.ru/3.1/library/threading.html#thread-objects).117118#### `class threading.Timer`119120Поток, выполняющий функцию по истечении заданного интервала времени.121122См. [*Объекты Timer*](https://python-all.ru/3.1/library/threading.html#timer-objects).123124#### `threading.settrace(func)`125126Устанавливает трассировочную функцию для всех потоков, запущенных из модуля `threading`. Функция *func* будет передана в [`sys.settrace()`](https://python-all.ru/3.1/library/sys.html#sys.settrace) для каждого потока перед вызовом его метода `run()`.127128#### `threading.setprofile(func)`129130Устанавливает профилирующую функцию для всех потоков, запущенных из модуля `threading`. Функция *func* будет передана в [`sys.setprofile()`](https://python-all.ru/3.1/library/sys.html#sys.setprofile) для каждого потока перед вызовом его метода `run()`.131132#### `threading.stack_size([size])`133134Возвращает размер стека потока, используемый при создании новых потоков. Необязательный135136*size*137138аргумент задаёт размер стека для последующих создаваемых потоков и должен быть равен 0 (используется платформенное или сконфигурированное значение по умолчанию) или положительному целому числу не менее 32 768 (32 кБ). Если изменение размера стека потока не поддерживается, возбуждается139140`ThreadError`141142. Если указанный размер стека некорректен, возбуждается143144[`ValueError`](https://python-all.ru/3.1/library/exceptions.html#ValueError)145146, а размер стека не изменяется. 32 кБ – это минимальный поддерживаемый размер стека, гарантирующий достаточное пространство для самого интерпретатора. Обратите внимание, что некоторые платформы могут накладывать особые ограничения на значения размера стека, например, требовать минимальный размер \> 32 кБ или выделение памяти, кратное размеру страницы системной памяти – для получения дополнительных сведений следует обращаться к документации платформы (страницы по 4 кБ распространены; при отсутствии более точной информации рекомендуется использовать размер стека, кратный 4096). Доступность: Windows, системы с потоками POSIX.147148Подробные интерфейсы объектов описаны ниже.149150Дизайн этого модуля основан на модели потоков Java, хотя и не строго. Однако, если в Java блокировки и переменные условий являются базовым поведением каждого объекта, в Python они являются отдельными объектами. Класс [`Thread`](https://python-all.ru/3.1/library/threading.html#threading.Thread) в Python поддерживает подмножество поведения класса Thread в Java; в настоящее время нет приоритетов, групп потоков, и потоки не могут быть уничтожены, остановлены, приостановлены, возобновлены или прерваны. Статические методы класса Thread в Java при их реализации отображаются на функции уровня модуля.151152Все описанные ниже методы выполняются атомарно.153154## 16.2.1. Объекты потока155156Этот класс представляет действие, которое выполняется в отдельном потоке управления. Есть два способа задать действие: передать вызываемый объект в конструктор или переопределить метод `run()` в подклассе. Никакие другие методы (кроме конструктора) не должны переопределяться в подклассе. Иными словами, *только* переопределяйте методы [`__init__()`](https://python-all.ru/3.1/reference/datamodel.html#object.__init__) и `run()` этого класса.157158После создания объекта потока его действие должно быть запущено вызовом метода `start()`. Это вызывает метод `run()` в отдельном потоке управления.159160После запуска действия поток считается «живым». Он перестаёт быть живым, когда его метод `run()` завершается – либо нормально, либо из-за необработанного исключения. Метод `is_alive()` проверяет, жив ли поток.161162Другие потоки могут вызывать метод `join()` потока. Это блокирует вызывающий поток до тех пор, пока поток, чей метод `join()` был вызван, не завершится.163164Поток имеет имя. Имя можно передать в конструктор, а также прочитать или изменить через атрибут `name`.165166Поток может быть помечен как «фоновый поток» (daemon thread). Смысл этого флага в том, что вся программа на Python завершается, когда остаются только фоновые потоки. Начальное значение наследуется от создающего потока. Флаг можно установить через свойство `daemon`.167168Существует объект «главный поток»; он соответствует начальному потоку управления в программе Python. Это не фоновый поток.169170Существует возможность создания «фиктивных объектов потоков» (dummy thread objects). Это объекты потоков, соответствующие «внешним потокам» – потокам управления, запущенным вне модуля threading, например, напрямую из кода на C. Фиктивные объекты потоков имеют ограниченную функциональность; они всегда считаются живыми и фоновыми, и к ним нельзя применить `join()`. Они никогда не удаляются, поскольку невозможно обнаружить завершение внешних потоков.171172#### `class threading.Thread(group=None, target=None, name=None, args=(), kwargs={})`173174Этот конструктор всегда следует вызывать с именованными аргументами. Аргументы:175176Параметр *group* должен быть `None`; зарезервирован для будущего расширения, когда будет реализован класс `ThreadGroup`.177178Параметр *target* – это вызываемый объект, который будет вызван методом [`run()`](https://python-all.ru/3.1/library/threading.html#threading.Thread.run). По умолчанию `None`, то есть ничего не вызывается.179180*name* – это имя потока. По умолчанию создаётся уникальное имя вида «Поток-*N*», где *N* – небольшое десятичное число.181182*args* – кортеж аргументов для вызова целевой функции. По умолчанию равен `()`.183184*kwargs* – это словарь именованных аргументов для вызова target. По умолчанию `{}`.185186Если подкласс переопределяет конструктор, он должен вызывать конструктор базового класса (`Thread.__init__()`) перед любыми другими действиями с потоком.187188#### `start()`189190Запускает выполнение потока.191192Этот метод должен вызываться не более одного раза для каждого объекта потока. Он обеспечивает вызов метода [`run()`](https://python-all.ru/3.1/library/threading.html#threading.Thread.run) объекта в отдельном потоке управления.193194Этот метод вызовет исключение [`RuntimeError`](https://python-all.ru/3.1/library/exceptions.html#RuntimeError), если его вызвать более одного раза для одного и того же объекта потока.195196#### `run()`197198Метод, представляющий действие потока.199200Вы можете переопределить этот метод в подклассе. Стандартный метод [`run()`](https://python-all.ru/3.1/library/threading.html#threading.Thread.run) вызывает вызываемый объект, переданный конструктору объекта как аргумент *target* (если он есть), с позиционными и именованными аргументами, взятыми из аргументов *args* и *kwargs* соответственно.201202#### `join(timeout=None)`203204Ожидание завершения потока. Это блокирует вызывающий поток до тех пор, пока поток, для которого был вызван метод [`join()`](https://python-all.ru/3.1/library/threading.html#threading.Thread.join), не завершится – либо успешно, либо из-за необработанного исключения – или пока не истечёт указанное время ожидания.205206Если аргумент *timeout* указан и не равен `None`, он должен быть числом с плавающей точкой, задающим время ожидания операции в секундах (или долях секунды). Поскольку [`join()`](https://python-all.ru/3.1/library/threading.html#threading.Thread.join) всегда возвращает `None`, после [`join()`](https://python-all.ru/3.1/library/threading.html#threading.Thread.join) необходимо вызвать [`is_alive()`](https://python-all.ru/3.1/library/threading.html#threading.Thread.is_alive), чтобы определить, произошло ли превышение времени ожидания – если поток всё ещё активен, вызов [`join()`](https://python-all.ru/3.1/library/threading.html#threading.Thread.join) был прерван по тайм-ауту.207208Если аргумент *timeout* отсутствует или равен `None`, операция будет блокироваться до завершения потока.209210К одному потоку можно применить [`join()`](https://python-all.ru/3.1/library/threading.html#threading.Thread.join) много раз.211212[`join()`](https://python-all.ru/3.1/library/threading.html#threading.Thread.join) вызывает [`RuntimeError`](https://python-all.ru/3.1/library/exceptions.html#RuntimeError), если предпринимается попытка присоединиться к текущему потоку, так как это привело бы к взаимной блокировке. Также ошибочно вызывать [`join()`](https://python-all.ru/3.1/library/threading.html#threading.Thread.join) для потока, который ещё не был запущен; попытка сделать это вызывает то же исключение.213214#### `name`215216Строка, используемая только для идентификации. Она не имеет семантического значения. Разным потокам можно задать одно и то же имя. Начальное имя устанавливается конструктором.217218#### `getName()`219220#### `setName()`221222Устаревший API геттера/сеттера для223224[`name`](https://python-all.ru/3.1/library/threading.html#threading.Thread.name)225226; вместо этого используйте его напрямую как свойство.227228#### `ident`229230Идентификатор потока, или231232`None`233234, если поток не был запущен. Это ненулевое целое число. См. функцию235236`thread.get_ident()`237238. Идентификаторы потоков могут быть повторно использованы, когда один поток завершается, а другой создаётся. Идентификатор доступен даже после завершения потока.239240#### `is_alive()`241242Возвращает, жив ли поток.243244Этот метод возвращает `True` непосредственно перед началом выполнения метода [`run()`](https://python-all.ru/3.1/library/threading.html#threading.Thread.run) и до самого завершения метода [`run()`](https://python-all.ru/3.1/library/threading.html#threading.Thread.run). Функция модуля [`enumerate()`](https://python-all.ru/3.1/library/threading.html#threading.enumerate) возвращает список всех активных потоков.245246#### `daemon`247248Логическое значение, указывающее, является ли поток демоническим (True) или нет (False). Должно быть установлено до вызова [`start()`](https://python-all.ru/3.1/library/threading.html#threading.Thread.start), иначе будет возбуждено [`RuntimeError`](https://python-all.ru/3.1/library/exceptions.html#RuntimeError). Начальное значение наследуется от создающего потока; главный поток не является демоническим, поэтому все потоки, созданные в главном потоке, по умолчанию имеют [`daemon`](https://python-all.ru/3.1/library/threading.html#threading.Thread.daemon) = `False`.249250Вся программа Python завершается, когда не остаётся ни одного живого потока, не являющегося демоном.251252#### `isDaemon()`253254#### `setDaemon()`255256Устаревший API геттера/сеттера для257258[`daemon`](https://python-all.ru/3.1/library/threading.html#threading.Thread.daemon)259260; вместо этого используйте его напрямую как свойство.261262## 16.2.2. Объекты блокировки263264Примитивная блокировка – это синхронизационный примитив, который не принадлежит конкретному потоку в заблокированном состоянии. В Python это в настоящее время самый низкоуровневый доступный примитив синхронизации, реализованный непосредственно модулем расширения [`_thread`](https://python-all.ru/3.1/library/_thread.html#module-_thread).265266Примитивная блокировка находится в одном из двух состояний: «заблокирована» или «разблокирована». Она создаётся в разблокированном состоянии. Имеет два базовых метода: `acquire()` и `release()`. В разблокированном состоянии `acquire()` переводит блокировку в заблокированное состояние и немедленно возвращается. В заблокированном состоянии `acquire()` блокируется до тех пор, пока вызов `release()` в другом потоке не переведёт её в разблокированное состояние; затем вызов `acquire()` снова переводит её в заблокированное состояние и возвращается. Метод `release()` следует вызывать только в заблокированном состоянии; он переводит блокировку в разблокированное состояние и немедленно возвращается. При попытке освободить разблокированную блокировку будет возбуждено исключение [`RuntimeError`](https://python-all.ru/3.1/library/exceptions.html#RuntimeError).267268Когда более одного потока заблокировано в `acquire()`, ожидая перехода состояния в разблокированное, только один поток продолжает работу при вызове `release()`, который сбрасывает состояние в разблокированное; какой именно из ожидающих потоков продолжит, не определено и может различаться в разных реализациях.269270Все методы выполняются атомарно.271272#### `Lock.acquire([blocking])`273274Захватывает блокировку, блокирующую или неблокирующую.275276При вызове без аргументов блокируется до тех пор, пока блокировка не будет разблокирована, затем переводит её в заблокированное состояние и возвращает истину.277278При вызове с аргументом *blocking*, установленным в true, делает то же самое, что и при вызове без аргументов, и возвращает true.279280При вызове с аргументом *blocking*, установленным в false, не блокируется. Если вызов без аргументов привёл бы к блокировке, немедленно возвращает false; в противном случае делает то же самое, что и при вызове без аргументов, и возвращает true.281282#### `Lock.release()`283284Освободить блокировку.285286Когда блокировка установлена, сбрасывает её в снятое состояние и возвращает управление. Если другие потоки заблокированы в ожидании освобождения блокировки, ровно одному из них разрешается продолжить работу.287288Не вызывайте этот метод, если блокировка уже разблокирована.289290Возвращаемое значение отсутствует.291292## 16.2.3. Объекты RLock293294Повторно входимая блокировка – это примитив синхронизации, который может быть захвачен одним и тем же потоком несколько раз. Внутри она использует понятия «поток-владелец» и «уровень рекурсии» в дополнение к состоянию «заблокировано/разблокировано», используемому простыми блокировками. В заблокированном состоянии блокировкой владеет какой-то поток; в разблокированном состоянии ею не владеет ни один поток.295296Чтобы захватить блокировку, поток вызывает свой метод `acquire()`; этот вызов возвращается, как только поток получает блокировку. Чтобы освободить блокировку, поток вызывает метод `release()`. Пары вызовов `acquire()`/`release()` могут быть вложенными; только последний `release()` (`release()` внешней пары) переводит блокировку в разблокированное состояние и позволяет другому потоку, заблокированному в `acquire()`, продолжить работу.297298#### `RLock.acquire(blocking=True)`299300Захватывает блокировку, блокирующую или неблокирующую.301302При вызове без аргументов: если этот поток уже владеет блокировкой, увеличить уровень рекурсии на единицу и немедленно вернуться. В противном случае, если блокировкой владеет другой поток, блокироваться до тех пор, пока блокировка не будет освобождена. Как только блокировка освобождена (не принадлежит ни одному потоку), захватить владение, установить уровень рекурсии в единицу и вернуться. Если несколько потоков заблокированы в ожидании освобождения блокировки, только один из них сможет захватить владение. В этом случае возвращаемого значения нет.303304При вызове с аргументом *blocking*, установленным в true, делает то же самое, что и при вызове без аргументов, и возвращает true.305306При вызове с аргументом *blocking*, установленным в false, не блокируется. Если вызов без аргументов привёл бы к блокировке, немедленно возвращает false; в противном случае делает то же самое, что и при вызове без аргументов, и возвращает true.307308#### `RLock.release()`309310Освобождает блокировку, уменьшая уровень рекурсии. Если после уменьшения он становится нулевым, сбрасывает блокировку в разблокированное состояние (не принадлежит ни одному потоку), и, если другие потоки заблокированы в ожидании освобождения блокировки, ровно одному из них разрешается продолжить. Если после уменьшения уровень рекурсии всё ещё ненулевой, блокировка остаётся заблокированной и принадлежит вызывающему потоку.311312Вызывайте этот метод только тогда, когда вызывающий поток владеет блокировкой. Исключение [`RuntimeError`](https://python-all.ru/3.1/library/exceptions.html#RuntimeError) возбуждается, если этот метод вызван, когда блокировка разблокирована.313314Возвращаемое значение отсутствует.315316## 16.2.4. Объекты Condition317318Переменная условия всегда связана с некоторой блокировкой; её можно передать явно, или блокировка будет создана по умолчанию. (Явная передача блокировки полезна, когда несколько переменных условия должны совместно использовать одну и ту же блокировку.)319320Переменная условия имеет методы `acquire()` и `release()`, которые вызывают соответствующие методы связанной блокировки. Также у неё есть метод `wait()`, а также методы `notify()` и `notify_all()`. Эти три метода можно вызывать только после того, как вызывающий поток захватил блокировку; в противном случае возбуждается [`RuntimeError`](https://python-all.ru/3.1/library/exceptions.html#RuntimeError).321322Метод `wait()` освобождает блокировку и затем блокируется до тех пор, пока не будет пробуждён вызовом `notify()` или `notify_all()` для той же переменной условия в другом потоке. После пробуждения он повторно захватывает блокировку и возвращается. Также можно указать время ожидания.323324Метод `notify()` пробуждает один из потоков, ожидающих переменную условия, если таковые есть. Метод `notify_all()` пробуждает все потоки, ожидающие переменную условия.325326Примечание: методы `notify()` и `notify_all()` не освобождают блокировку; это означает, что пробуждённый поток (или потоки) не вернутся из вызова `wait()` немедленно, а только тогда, когда поток, вызвавший `notify()` или `notify_all()`, наконец отпустит блокировку.327328Совет: типичный стиль программирования с переменными условия использует блокировку для синхронизации доступа к некоторому общему состоянию; потоки, заинтересованные в определённом изменении состояния, вызывают `wait()` повторно, пока не увидят желаемое состояние, а потоки, изменяющие состояние, вызывают `notify()` или `notify_all()`, когда изменяют состояние таким образом, что оно может стать желаемым для одного из ожидающих. Например, следующий код демонстрирует обобщённую ситуацию «производитель-потребитель» с неограниченной ёмкостью буфера:329330```python331# Потребить один элемент332cv.acquire()333while not an_item_is_available():334 cv.wait()335get_an_available_item()336cv.release()337338# Произвести один элемент339cv.acquire()340make_an_item_available()341cv.notify()342cv.release()343```344345Чтобы выбрать между `notify()` и `notify_all()`, подумайте, может ли одно изменение состояния быть интересным только одному или нескольким ожидающим потокам. Например, в типичной ситуации «производитель-потребитель» добавление одного элемента в буфер требует пробуждения только одного потока-потребителя.346347#### `class threading.Condition(lock=None)`348349Если аргумент *блокировка* задан и не равен `None`, то он должен быть объектом [`блокировка`](https://python-all.ru/3.1/library/threading.html#threading.Lock) или [`RLock`](https://python-all.ru/3.1/library/threading.html#threading.RLock) и использоваться в качестве базовой блокировки. В противном случае создаётся новый объект [`RLock`](https://python-all.ru/3.1/library/threading.html#threading.RLock), который используется как базовая блокировка.350351#### `acquire(*args)`352353Захватывает базовую блокировку. Этот метод вызывает соответствующий метод базовой блокировки; возвращаемое значение – то, что возвращает этот метод.354355#### `release()`356357Освобождает базовую блокировку. Этот метод вызывает соответствующий метод базовой блокировки; возвращаемое значение отсутствует.358359#### `wait(timeout=None)`360361Ожидает до получения уведомления или до истечения тайм-аута. Если вызывающий поток не захватил блокировку на момент вызова этого метода, возбуждается исключение [`RuntimeError`](https://python-all.ru/3.1/library/exceptions.html#RuntimeError).362363Этот метод освобождает базовую блокировку, а затем блокируется до тех пор, пока не будет пробуждён вызовом [`notify()`](https://python-all.ru/3.1/library/threading.html#threading.Condition.notify) или [`notify_all()`](https://python-all.ru/3.1/library/threading.html#threading.Condition.notify_all) для той же условной переменной из другого потока, или пока не наступит опциональный тайм-аут. После пробуждения или истечения тайм-аута он повторно захватывает блокировку и возвращает управление.364365Если аргумент *timeout* присутствует и не равен `None`, то он должен быть числом с плавающей запятой, указывающим тайм-аут для операции в секундах (или долях секунды).366367Если базовая блокировка является объектом [`RLock`](https://python-all.ru/3.1/library/threading.html#threading.RLock), то она не освобождается с помощью его метода [`release()`](https://python-all.ru/3.1/library/threading.html#threading.Condition.release), поскольку этот метод может фактически не разблокировать блокировку, если она была захвачена несколько раз рекурсивно. Вместо этого используется внутренний интерфейс класса [`RLock`](https://python-all.ru/3.1/library/threading.html#threading.RLock), который действительно разблокирует её, даже если она была рекурсивно захвачена несколько раз. Затем другой внутренний интерфейс восстанавливает уровень рекурсии при повторном захвате блокировки.368369#### `notify()`370371Пробуждает поток, ожидающий на этом условии, если такой существует. Если вызывающий поток не захватил блокировку при вызове этого метода, возбуждается [`RuntimeError`](https://python-all.ru/3.1/library/exceptions.html#RuntimeError).372373Этот метод пробуждает один из потоков, ожидающих переменную условия, если таковые ожидают; если ни один поток не ожидает, то он не выполняет никаких действий.374375Текущая реализация пробуждает ровно один поток, если такие ожидают. Однако полагаться на это поведение небезопасно. Будущая, оптимизированная реализация может иногда пробуждать более одного потока.376377Примечание: пробужденный поток на самом деле не возвращается из своего вызова [`wait()`](https://python-all.ru/3.1/library/threading.html#threading.Condition.wait), пока не сможет повторно захватить блокировку. Поскольку [`notify()`](https://python-all.ru/3.1/library/threading.html#threading.Condition.notify) не освобождает блокировку, вызывающий поток должен это сделать.378379#### `notify_all()`380381Пробуждает все потоки, ожидающие на этом условии. Этот метод действует как382383[`notify()`](https://python-all.ru/3.1/library/threading.html#threading.Condition.notify)384385, но пробуждает все ожидающие потоки вместо одного. Если вызывающий поток не захватил блокировку при вызове этого метода, возникает исключение386387[`RuntimeError`](https://python-all.ru/3.1/library/exceptions.html#RuntimeError)388389.390391## 16.2.5. Объекты семафоров392393Это один из старейших примитивов синхронизации в истории компьютерной науки, изобретенный ранним нидерландским учёным-компьютерщиком Эдсгером В. Дейкстрой (он использовал `P()` и `V()` вместо `acquire()` и `release()`).394395Семафор управляет внутренним счётчиком, который уменьшается при каждом вызове `acquire()` и увеличивается при каждом вызове `release()`. Счётчик никогда не может стать меньше нуля; когда `acquire()` обнаруживает, что он равен нулю, то блокируется, ожидая, пока другой поток вызовет `release()`.396397#### `class threading.Semaphore(value=1)`398399Необязательный аргумент задаёт начальное *value* для внутреннего счётчика; по умолчанию оно равно `1`. Если указанное *value* меньше 0, возникает исключение [`ValueError`](https://python-all.ru/3.1/library/exceptions.html#ValueError).400401#### `acquire(blocking=True)`402403Захватывает семафор.404405При вызове без аргументов: если внутренний счётчик при входе больше нуля, уменьшить его на единицу и немедленно вернуться. Если при входе он равен нулю, заблокироваться, ожидая, пока какой-либо другой поток вызовет [`release()`](https://python-all.ru/3.1/library/threading.html#threading.Semaphore.release), чтобы сделать его больше нуля. Это делается с надлежащей взаимоблокировкой, так что если несколько вызовов [`acquire()`](https://python-all.ru/3.1/library/threading.html#threading.Semaphore.acquire) заблокированы, [`release()`](https://python-all.ru/3.1/library/threading.html#threading.Semaphore.release) пробудит ровно один из них. Реализация может выбрать его случайным образом, поэтому полагаться на порядок пробуждения заблокированных потоков не следует. В этом случае возвращаемого значения нет.406407При вызове с *blocking*, установленным в true, делает то же самое, что и при вызове без аргументов, и возвращает true.408409При вызове с *blocking*, установленным в false, не блокируется. Если вызов без аргументов привёл бы к блокировке, немедленно возвращает false; в противном случае делает то же самое, что и при вызове без аргументов, и возвращает true.410411#### `release()`412413Освобождает семафор, увеличивая внутренний счётчик на единицу. Когда он был равен нулю при входе и другой поток ожидает, когда он снова станет больше нуля, пробуждает этот поток.414415### 16.2.5.1. [`Semaphore`](https://python-all.ru/3.1/library/threading.html#threading.Semaphore) Пример416417Семафоры часто используются для защиты ресурсов с ограниченной ёмкостью, например, сервера базы данных. В любой ситуации, когда размер ресурса фиксирован, следует использовать ограниченный семафор. Перед запуском рабочих потоков главный поток инициализирует семафор:418419```python420maxconnections = 5421...422pool_sema = BoundedSemaphore(value=maxconnections)423```424425После запуска рабочие потоки вызывают методы acquire и release семафора, когда им нужно подключиться к серверу:426427```python428pool_sema.acquire()429conn = connectdb()430... use connection ...431conn.close()432pool_sema.release()433```434435Использование ограниченного семафора снижает вероятность того, что ошибка программирования, приводящая к освобождению семафора большее количество раз, чем его захват, останется незамеченной.436437## 16.2.6. Объекты событий438439Это один из простейших механизмов взаимодействия между потоками: один поток сигнализирует о событии, а другие потоки ожидают его.440441Объект события управляет внутренним флагом, который можно установить в true с помощью метода [`set()`](https://python-all.ru/3.1/library/threading.html#threading.Event.set) и сбросить в false с помощью метода `clear()`. Метод `wait()` блокируется, пока флаг не станет true.442443#### `class threading.Event`444445Внутренний флаг изначально равен false.446447#### `is_set()`448449Возвращает true тогда и только тогда, когда внутренний флаг равен true.450451#### `set()`452453Устанавливает внутренний флаг в true. Все потоки, ожидающие его установки в true, пробуждаются. Потоки, которые вызывают454455[`wait()`](https://python-all.ru/3.1/library/threading.html#threading.Event.wait)456457после того, как флаг уже true, не будут блокироваться вообще.458459#### `clear()`460461Сбрасывает внутренний флаг в false. После этого потоки, вызывающие462463[`wait()`](https://python-all.ru/3.1/library/threading.html#threading.Event.wait)464465, будут блокироваться до тех пор, пока не будет вызван466467[`set()`](https://python-all.ru/3.1/library/threading.html#threading.Event.set)468469, чтобы снова установить флаг в true.470471#### `wait(timeout=None)`472473Блокируется, пока внутренний флаг не станет true. Если при входе флаг уже true, вернуться немедленно. В противном случае блокироваться до тех пор, пока другой поток не вызовет [`set()`](https://python-all.ru/3.1/library/stdtypes.html#set), чтобы установить флаг в true, или пока не истечет необязательный тайм-аут.474475Если аргумент timeout присутствует и не равен `None`, он должен быть числом с плавающей точкой, задающим таймаут для операции в секундах (или долях секунды).476477Этот метод возвращает внутренний флаг при выходе, поэтому он всегда возвращает `True`, за исключением случая, если задан тайм-аут и операция истекает по времени.478479Изменено в версии 3.1: Ранее метод всегда возвращал `None`.480481## 16.2.7. Объекты таймеров482483Этот класс представляет действие, которое должно быть выполнено только после того, как пройдет определенное время – таймер. [`Timer`](https://python-all.ru/3.1/library/threading.html#threading.Timer) является подклассом [`Thread`](https://python-all.ru/3.1/library/threading.html#threading.Thread) и, таким образом, служит примером создания пользовательских потоков.484485Таймеры запускаются, как и потоки, вызовом метода `start()`. Таймер можно остановить (до начала его действия) вызовом метода `cancel()`. Интервал, который таймер будет ждать перед выполнением своего действия, может не совпадать в точности с интервалом, указанным пользователем.486487Например:488489```python490def hello():491 print("hello, world")492493t = Timer(30.0, hello)494t.start() # через 30 секунд будет выведено "hello, world"495```496497#### `class threading.Timer(interval, function, args=[], kwargs={})`498499Создаёт таймер, который выполнит *function* с аргументами *args* и именованными аргументами *kwargs* по прошествии *interval* секунд.500501#### `cancel()`502503Останавливает таймер и отменяет выполнение его действия. Это сработает, только если таймер всё ещё находится в стадии ожидания.504505## 16.2.8. Использование блокировок, условий и семафоров в операторе [`with`](https://python-all.ru/3.1/reference/compound_stmts.html#with)506507Все объекты, предоставляемые этим модулем, которые имеют методы `acquire()` и `release()`, могут использоваться в качестве менеджеров контекста для [`with`](https://python-all.ru/3.1/reference/compound_stmts.html#with) оператора. Метод `acquire()` будет вызван при входе в блок, а `release()` будет вызван при выходе из блока.508509Объекты [`Lock`](https://python-all.ru/3.1/library/threading.html#threading.Lock), [`RLock`](https://python-all.ru/3.1/library/threading.html#threading.RLock), [`Condition`](https://python-all.ru/3.1/library/threading.html#threading.Condition), [`Semaphore`](https://python-all.ru/3.1/library/threading.html#threading.Semaphore) и [`BoundedSemaphore`](https://python-all.ru/3.1/library/threading.html#threading.BoundedSemaphore) могут использоваться в качестве [`with`](https://python-all.ru/3.1/reference/compound_stmts.html#with) менеджеров контекста. Например:510511```python512import threading513514some_rlock = threading.RLock()515516with some_rlock:517 print("some_rlock is locked while this executes")518```519520## 16.2.9. Импорт в многопоточном коде521522Хотя механизм импорта потокобезопасен, есть два ключевых ограничения для импорта из потоков, вытекающие из внутренних особенностей реализации потокобезопасности:523524- Во-первых, за исключением главного модуля, импорт не должен вызывать побочный эффект в виде создания нового потока с последующим ожиданием этого потока любым способом. Несоблюдение этого ограничения может привести к взаимоблокировке, если созданный поток прямо или косвенно попытается импортировать модуль.525- Во-вторых, все попытки импорта должны быть завершены до того, как интерпретатор начнёт завершение работы. Проще всего этого добиться, выполняя импорт только из недемоновых потоков, созданных через модуль threading. Потоки-демоны и потоки, созданные напрямую с помощью модуля thread, потребуют какой-либо иной синхронизации, чтобы гарантировать, что они не будут пытаться выполнять импорт после начала завершения работы интерпретатора. Несоблюдение этого ограничения приведёт к периодическим исключениям и сбоям при завершении работы интерпретатора (поскольку поздние попытки импорта пытаются получить доступ к механизмам, которые уже находятся в недопустимом состоянии).526