threading — Thread-based parallelism

Вихідний код: Lib/threading.py


This module constructs higher-level threading interfaces on top of the lower level _thread module. See also the queue module.

Змінено в версії 3.7: Раніше цей модуль був необов’язковим, тепер він доступний завжди.

Примітка

While they are not listed below, the camelCase names used for some methods and functions in this module in the Python 2.x series are still supported by this module.

CPython implementation detail: У CPython, завдяки Global Interpreter Lock, лише один потік може виконувати код Python одночасно (навіть якщо певні бібліотеки, орієнтовані на продуктивність, можуть подолати це обмеження). Якщо ви хочете, щоб ваша програма краще використовувала обчислювальні ресурси багатоядерних машин, радимо використовувати multiprocessing або concurrent.futures.ProcessPoolExecutor. Однак потокова розв’язка все ще є відповідною моделлю, якщо ви хочете одночасно запускати кілька завдань, пов’язаних із вводом-виводом.

Цей модуль визначає такі функції:

threading.active_count()

Повертає кількість активних об’єктів Thread. Повернена кількість дорівнює довжині списку, повернутого enumerate().

threading.current_thread()

Повертає поточний об’єкт Thread, що відповідає потоку керування абонента. Якщо потік керування викликаючого не було створено за допомогою модуля threading, повертається фіктивний об’єкт потоку з обмеженою функціональністю.

threading.excepthook(args, /)

Обробляти неперехоплений виняток, викликаний Thread.run().

Аргумент args має такі атрибути:

  • exc_type: Тип винятку.

  • exc_value: значення винятку, може бути None.

  • exc_traceback: Зворотне відстеження винятків, може бути None.

  • потік: Потік, який викликав виняток, може бути None.

Якщо exc_type дорівнює SystemExit, виняток мовчки ігнорується. В іншому випадку виняток друкується на sys.stderr.

Якщо ця функція викликає виняткову ситуацію, для її обробки викликається sys.excepthook().

threading.excepthook() можна перевизначити, щоб контролювати, як обробляються неперехоплені винятки, викликані Thread.run().

Зберігання exc_value за допомогою спеціального хука може створити еталонний цикл. Його слід явно очистити, щоб розірвати цикл посилання, коли виняток більше не потрібен.

Зберігання потоку за допомогою спеціального хука може відновити його, якщо він встановлений для об’єкта, який завершується. Уникайте зберігання thread після завершення власного хука, щоб уникнути відновлення об’єктів.

Дивись також

sys.excepthook() обробляє неперехоплені винятки.

Нове в версії 3.8.

threading.get_ident()

Повертає «ідентифікатор потоку» поточного потоку. Це ненульове ціле число. Його значення не має прямого значення; воно призначене як магічне печиво, яке можна використовувати, наприклад. щоб індексувати словник даних, що стосуються потоку. Ідентифікатори потоку можуть бути перероблені, коли потік завершується та створюється інший.

Нове в версії 3.3.

threading.get_native_id()

Повертає власний інтегральний ідентифікатор потоку поточного потоку, призначеного ядром. Це невід’ємне ціле число. Його значення може використовуватися для однозначної ідентифікації цього конкретного потоку в системі (до завершення роботи потоку, після чого значення може бути повторно використано ОС).

Availability: Windows, FreeBSD, Linux, macOS, OpenBSD, NetBSD, AIX.

Нове в версії 3.8.

threading.enumerate()

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

threading.main_thread()

Повертає головний об’єкт Thread. У нормальних умовах основний потік — це потік, з якого було запущено інтерпретатор Python.

Нове в версії 3.4.

threading.settrace(func)

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

threading.setprofile(func)

Встановіть функцію профілю для всіх потоків, запущених із модуля threading. func буде передано до sys.setprofile() для кожного потоку перед викликом його методу run().

threading.stack_size([size])

Повертає розмір стека потоків, який використовувався під час створення нових потоків. Необов’язковий аргумент size визначає розмір стека, який буде використовуватися для згодом створених потоків, і має дорівнювати 0 (використовувати платформу або налаштоване за замовчуванням) або додатне ціле значення принаймні 32 768 (32 КіБ). Якщо size не вказано, використовується 0. Якщо зміна розміру стека потоку не підтримується, виникає RuntimeError. Якщо вказаний розмір стека недійсний, виникає помилка ValueError і розмір стека не змінюється. 32 КіБ наразі є мінімальним підтримуваним значенням розміру стеку, щоб гарантувати достатній простір стеку для самого інтерпретатора. Зауважте, що деякі платформи можуть мати певні обмеження щодо значень розміру стека, як-от вимагати мінімальний розмір стека > 32 КБ або вимагати виділення кратного розміру сторінки системної пам’яті – для отримання додаткової інформації слід звернутися до документації платформи (сторінки 4 КБ є поширеними; використання кратних 4096 для розміру стека є запропонованим підходом за відсутності більш конкретної інформації).

Availability: Windows, systems with POSIX threads.

Цей модуль також визначає таку константу:

threading.TIMEOUT_MAX

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

Нове в версії 3.2.

Цей модуль визначає ряд класів, які детально описані в розділах нижче.

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

Усі методи, описані нижче, виконуються атомарно.

Локальні дані потоку

Локальні дані потоку — це дані, значення яких залежать від потоку. Щоб керувати локальними даними потоку, просто створіть екземпляр local (або підклас) і збережіть у ньому атрибути:

mydata = threading.local()
mydata.x = 1

Значення екземпляра будуть різними для окремих потоків.

class threading.local

Клас, який представляє дані локального потоку.

For more details and extensive examples, see the documentation string of the _threading_local module.

Об’єкти потоку

The Thread class represents an activity that is run in a separate thread of control. There are two ways to specify the activity: by passing a callable object to the constructor, or by overriding the run() method in a subclass. No other methods (except for the constructor) should be overridden in a subclass. In other words, only override the __init__() and run() methods of this class.

Після створення об’єкта потоку його діяльність має бути запущена викликом методу потоку start(). Це викликає метод run() в окремому потоці керування.

Коли активність потоку розпочато, він вважається «живим». Він перестає бути живим, коли його метод run() завершує роботу - або звичайним способом, або шляхом виклику необробленого винятку. Метод is_alive() перевіряє, чи живий потік.

Інші потоки можуть викликати метод потоку join(). Це блокує потік, що викликає, доки не буде завершено потік, чий метод join() викликається.

Нитка має назву. Ім’я можна передати конструктору та прочитати або змінити за допомогою атрибута name.

Якщо метод run() викликає виняток, для його обробки викликається threading.excepthook(). За замовчуванням threading.excepthook() мовчки ігнорує SystemExit.

Потік можна позначити як «потік демона». Значення цього прапорця полягає в тому, що вся програма Python завершує роботу, коли залишаються лише потоки демона. Початкове значення успадковується від потоку створення. Прапор можна встановити через властивість daemon або аргумент конструктора daemon.

Примітка

Потоки демона раптово зупиняються під час завершення роботи. Їхні ресурси (такі як відкриті файли, транзакції бази даних тощо) можуть не вивільнятися належним чином. Якщо ви хочете, щоб ваші потоки зупинялися акуратно, зробіть їх недемонічними та використовуйте відповідний механізм сигналізації, такий як Event.

Є об’єкт «основний потік»; це відповідає початковому потоку керування в програмі Python. Це не потік демона.

There is the possibility that «dummy thread objects» are created. These are thread objects corresponding to «alien threads», which are threads of control started outside the threading module, such as directly from C code. Dummy thread objects have limited functionality; they are always considered alive and daemonic, and cannot be join()ed. They are never deleted, since it is impossible to detect the termination of alien threads.

class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)

Цей конструктор слід завжди викликати з ключовими аргументами. Аргументами є:

group should be None; reserved for future extension when a ThreadGroup class is implemented.

target — об’єкт, який викликається методом run(). За замовчуванням None, тобто нічого не викликається.

name is the thread name. By default, a unique name is constructed of the form «Thread-N» where N is a small decimal number.

args is the argument tuple for the target invocation. Defaults to ().

kwargs — це словник ключових аргументів для цільового виклику. За замовчуванням {}.

Якщо не None, daemon явно встановлює, чи є потік демонічним. Якщо None (за замовчуванням), демонічна властивість успадковується з поточного потоку.

Якщо підклас перевизначає конструктор, він повинен обов’язково викликати конструктор базового класу (Thread.__init__()), перш ніж робити щось інше з потоком.

Змінено в версії 3.3: Added the daemon argument.

start()

Розпочати діяльність потоку.

Він має бути викликаний щонайбільше один раз на об’єкт потоку. Він організовує виклик методу run() об’єкта в окремому потоці керування.

Цей метод викличе RuntimeError, якщо викликати кілька разів для того самого об’єкта потоку.

run()

Метод, що представляє діяльність потоку.

Ви можете перевизначити цей метод у підкласі. Стандартний метод run() викликає викликаний об’єкт, який передається конструктору об’єкта як аргумент target, якщо такий є, з позиційними аргументами та ключовими аргументами, взятими з аргументів args і kwargs відповідно.

join(timeout=None)

Зачекайте, поки потік завершиться. Це блокує потік, що викликає, доки потік, чий метод join() викликається, не завершиться (звичайно або через необроблений виняток), або доки не настане додатковий час очікування.

When the timeout argument is present and not None, it should be a floating point number specifying a timeout for the operation in seconds (or fractions thereof). As join() always returns None, you must call is_alive() after join() to decide whether a timeout happened – if the thread is still alive, the join() call timed out.

Якщо аргумент timeout відсутній або None, операція буде заблокована до завершення потоку.

A thread can be join()ed many times.

join() викликає RuntimeError, якщо робиться спроба приєднатися до поточного потоку, оскільки це призведе до взаємоблокування. Також є помилкою join() потоку до того, як він був запущений, і спроби зробити це викликають той самий виняток.

name

Рядок, який використовується лише для ідентифікації. Він не має семантики. Декільком потокам можна присвоїти однакові назви. Початкове ім’я задається конструктором.

getName()
setName()

Old getter/setter API for name; use it directly as a property instead.

ident

«Ідентифікатор потоку» цього потоку або «Немає», якщо потік не було запущено. Це ненульове ціле число. Перегляньте функцію get_ident(). Ідентифікатори потоку можуть бути перероблені, коли потік завершується та створюється інший. Ідентифікатор доступний навіть після завершення потоку.

native_id

The native integral thread ID of this thread. This is a non-negative integer, or None if the thread has not been started. See the get_native_id() function. This represents the Thread ID (TID) as assigned to the thread by the OS (kernel). Its value may be used to uniquely identify this particular thread system-wide (until the thread terminates, after which the value may be recycled by the OS).

Примітка

Подібно до ідентифікаторів процесів, ідентифікатори потоків дійсні (гарантовано унікальні для всієї системи) лише з моменту створення потоку до його завершення.

Availability: Requires get_native_id() function.

Нове в версії 3.8.

is_alive()

Повернути, чи живий потік.

Цей метод повертає True безпосередньо перед запуском методу run() до завершення роботи методу run(). Функція модуля enumerate() повертає список усіх активних потоків.

daemon

A boolean value indicating whether this thread is a daemon thread (True) or not (False). This must be set before start() is called, otherwise RuntimeError is raised. Its initial value is inherited from the creating thread; the main thread is not a daemon thread and therefore all threads created in the main thread default to daemon = False.

Уся програма Python завершує роботу, якщо не залишиться активних потоків, які не є демонами.

isDaemon()
setDaemon()

Old getter/setter API for daemon; use it directly as a property instead.

Блокування об’єктів

Примітивне блокування — це примітив синхронізації, який не належить певному потоку, коли він заблокований. У Python наразі це найнижчий доступний примітив синхронізації рівня, реалізований безпосередньо модулем розширення _thread.

Примітивний замок знаходиться в одному з двох станів: «заблоковано» або «розблоковано». Створюється в розблокованому стані. Він має два основні методи: acquire() і release(). Коли стан розблоковано, acquire() змінює стан на заблокований і повертає негайно. Коли стан заблоковано, acquire() блокується, доки виклик release() в іншому потоці не змінить його на розблокований, після чого виклик acquire() скидається він блокується та повертається. Метод release() слід викликати лише в заблокованому стані; він змінює стан на розблокований і негайно повертається. Якщо буде зроблена спроба зняти розблоковане блокування, буде викликано RuntimeError.

Блокування також підтримують протокол управління контекстом.

Якщо в acquire() заблоковано більше одного потоку, який очікує, поки стан стане розблокованим, лише один потік продовжує роботу, коли виклик release() скидає стан до розблокованого; який із потоків, що очікують, продовжується, не визначено та може відрізнятися в різних реалізаціях.

Усі методи виконуються атомарно.

class threading.Lock

Клас, що реалізує примітивні об’єкти блокування. Після того, як потік отримав блокування, наступні спроби отримати його блокуються, доки він не буде звільнений; будь-який потік може його звільнити.

Note that Lock is actually a factory function which returns an instance of the most efficient version of the concrete Lock class that is supported by the platform.

acquire(blocking=True, timeout=-1)

Отримайте блокування, блокування або неблокування.

При виклику з аргументом blocking, встановленим на True (за замовчуванням), блокувати, доки блокування не буде розблоковано, потім установіть його на locked і поверніть True.

При виклику з аргументом blocking, встановленим на False, не блокувати. Якщо виклик із блокуванням, встановленим на True, заблокує, негайно поверніть False; інакше встановіть блокування на locked і поверніть True.

When invoked with the floating-point timeout argument set to a positive value, block for at most the number of seconds specified by timeout and as long as the lock cannot be acquired. A timeout argument of -1 specifies an unbounded wait. It is forbidden to specify a timeout when blocking is false.

Поверненим значенням є True, якщо блокування отримано успішно, False, якщо ні (наприклад, якщо минув тайм-аут).

Змінено в версії 3.2: Параметр timeout є новим.

Змінено в версії 3.2: Отримання блокування тепер може бути перервано сигналами на POSIX, якщо це підтримує базова реалізація потоків.

release()

Відпустіть блокування. Це можна викликати з будь-якого потоку, а не лише з потоку, який отримав блокування.

Коли замок заблоковано, скиньте його до розблокованого та поверніться. Якщо будь-які інші потоки заблоковано в очікуванні розблокування блокування, дозвольте рівно одному з них продовжити.

Під час виклику для розблокованого блокування виникає RuntimeError.

Поверненого значення немає.

locked()

Return true if the lock is acquired.

Об’єкти RLock

Блокування повторного входу — це примітив синхронізації, який може бути отриманий кілька разів одним потоком. Внутрішньо він використовує поняття «володіння потоком» і «рівень рекурсії» на додаток до заблокованого/розблокованого стану, який використовується примітивними блокуваннями. У заблокованому стані деякий потік володіє блокуванням; у розблокованому стані жоден потік не володіє ним.

To lock the lock, a thread calls its acquire() method; this returns once the thread owns the lock. To unlock the lock, a thread calls its release() method. acquire()/release() call pairs may be nested; only the final release() (the release() of the outermost pair) resets the lock to unlocked and allows another thread blocked in acquire() to proceed.

Reentrant locks also support the context management protocol.

class threading.RLock

Цей клас реалізує об’єкти повторного входу. Блокування повторного входу має бути звільнено потоком, який його отримав. Як тільки потік отримав блокування повторного входу, той самий потік може отримати його знову без блокування; потік повинен звільнити його один раз за кожен раз, коли він його отримав.

Зауважте, що RLock насправді є фабричною функцією, яка повертає екземпляр найефективнішої версії конкретного класу RLock, який підтримується платформою.

acquire(blocking=True, timeout=-1)

Отримайте блокування, блокування або неблокування.

When invoked without arguments: if this thread already owns the lock, increment the recursion level by one, and return immediately. Otherwise, if another thread owns the lock, block until the lock is unlocked. Once the lock is unlocked (not owned by any thread), then grab ownership, set the recursion level to one, and return. If more than one thread is blocked waiting until the lock is unlocked, only one at a time will be able to grab ownership of the lock. There is no return value in this case.

When invoked with the blocking argument set to true, do the same thing as when called without arguments, and return True.

When invoked with the blocking argument set to false, do not block. If a call without an argument would block, return False immediately; otherwise, do the same thing as when called without arguments, and return True.

When invoked with the floating-point timeout argument set to a positive value, block for at most the number of seconds specified by timeout and as long as the lock cannot be acquired. Return True if the lock has been acquired, false if the timeout has elapsed.

Змінено в версії 3.2: Параметр timeout є новим.

release()

Зніміть блокування, зменшивши рівень рекурсії. Якщо після декремента він дорівнює нулю, скиньте блокування до розблокованого (не належить жодному потоку), і якщо будь-які інші потоки заблоковані в очікуванні розблокування блокування, дозвольте рівно одному з них продовжити. Якщо після декременту рівень рекурсії все ще ненульовий, блокування залишається заблокованим і належить потоку, що викликає.

Only call this method when the calling thread owns the lock. A RuntimeError is raised if this method is called when the lock is unlocked.

Поверненого значення немає.

Об’єкти стану

Змінна умови завжди асоціюється з якимось блокуванням; це можна передати, або він буде створений за замовчуванням. Передача одного корисна, коли кілька змінних умови повинні використовувати один і той же блокування. Блокування є частиною об’єкта умови: вам не потрібно відстежувати його окремо.

Змінна умови підпорядковується протоколу керування контекстом: використання оператора with отримує пов’язане блокування протягом тривалості включеного блоку. Методи acquire() і release() також викликають відповідні методи пов’язаного блокування.

Інші методи повинні викликатися з відповідним блокуванням. Метод wait() знімає блокування, а потім блокує, доки інший потік не розбудить його, викликавши notify() або notify_all(). Після пробудження wait() знову отримує блокування та повертається. Також можна вказати тайм-аут.

Метод notify() активує один із потоків, які очікують на змінну умови, якщо такі очікують. Метод notify_all() активує всі потоки, які очікують на змінну умови.

Примітка: методи notify() і notify_all() не знімають блокування; це означає, що активований потік або потоки не повернеться зі свого виклику wait() негайно, а лише тоді, коли потік, який викликав notify() або notify_all() остаточно відмовляється від власності на замок.

Типовий стиль програмування з використанням умовних змінних використовує блокування для синхронізації доступу до деякого спільного стану; потоки, які зацікавлені в певній зміні стану, викликають wait() неодноразово, доки не побачать бажаний стан, тоді як потоки, які змінюють стан, викликають notify() або notify_all(), коли вони змінюють стан таким чином, що це може бути бажаним станом для одного з офіціантів. Наприклад, наступний код є загальною ситуацією виробник-споживач з необмеженою ємністю буфера:

# Consume one item
with cv:
    while not an_item_is_available():
        cv.wait()
    get_an_available_item()

# Produce one item
with cv:
    make_an_item_available()
    cv.notify()

Цикл while для перевірки умови програми необхідний, оскільки wait() може повернутися через довільний тривалий час, а умова, яка спонукала до виклику notify(), може не довше зберігаються. Це властиво багатопоточному програмуванню. Метод wait_for() можна використовувати для автоматизації перевірки умов і полегшує обчислення тайм-аутів:

# Consume an item
with cv:
    cv.wait_for(an_item_is_available)
    get_an_available_item()

Щоб вибрати між notify() і notify_all(), подумайте, чи одна зміна стану може бути цікавою лише для одного чи кількох потоків, що очікують. наприклад у типовій ситуації виробник-споживач додавання одного елемента до буфера потребує лише пробудження одного потоку споживача.

class threading.Condition(lock=None)

Цей клас реалізує об’єкти умовної змінної. Змінна умови дозволяє одному або декільком потокам чекати, поки вони не будуть сповіщені іншим потоком.

Якщо вказано аргумент lock, а не None, це має бути об’єкт Lock або RLock, і він використовується як основний блокування. В іншому випадку створюється новий об’єкт RLock, який використовується як основний блокування.

Змінено в версії 3.3: змінено з функції фабрики на клас.

acquire(*args)

Отримайте основний замок. Цей метод викликає відповідний метод основного блокування; значення, що повертається, є тим, що повертає цей метод.

release()

Звільніть базовий замок. Цей метод викликає відповідний метод основного блокування; немає зворотного значення.

wait(timeout=None)

Зачекайте, поки не буде сповіщено або поки не настане час очікування. Якщо потік, що викликає, не отримав блокування під час виклику цього методу, виникає RuntimeError.

Цей метод знімає основне блокування, а потім блокує, доки його не розбудить виклик notify() або notify_all() для тієї самої змінної умови в іншому потоці, або доки не настане додатковий тайм-аут. Після пробудження або вичерпання часу блокування знову блокується та повертається.

When the timeout argument is present and not None, it should be a floating point number specifying a timeout for the operation in seconds (or fractions thereof).

Якщо основним блокуванням є RLock, він не звільняється за допомогою свого методу release(), оскільки це може не розблокувати блокування, якщо його було отримано кілька разів рекурсивно. Замість цього використовується внутрішній інтерфейс класу RLock, який справді розблоковує його, навіть якщо він був рекурсивно отриманий кілька разів. Інший внутрішній інтерфейс потім використовується для відновлення рівня рекурсії, коли блокування знову отримано.

Поверненим значенням є True, якщо не минув заданий тайм-аут, у такому випадку воно має значення False.

Змінено в версії 3.2: Раніше метод завжди повертав None.

wait_for(predicate, timeout=None)

Зачекайте, поки умова оціниться як істинна. предикат має бути викликаним, результат якого буде інтерпретуватися як логічне значення. Може бути надано тайм-аут, що дає максимальний час очікування.

Цей допоміжний метод може викликати wait() неодноразово, доки предикат не буде задоволено або поки не настане час очікування. Значення, що повертається, є останнім значенням, що повертається предикатом, і оцінюється як False, якщо час очікування методу минув.

Ігноруючи функцію тайм-ауту, виклик цього методу приблизно еквівалентний написанню:

while not predicate():
    cv.wait()

Таким чином, застосовуються ті самі правила, що й для wait(): блокування має бути утримано під час виклику та повторно отримане після повернення. Предикат обчислюється з утриманим блокуванням.

Нове в версії 3.2.

notify(n=1)

За замовчуванням активувати один потік, який очікує за цієї умови, якщо такий є. Якщо потік, що викликає, не отримав блокування під час виклику цього методу, виникає RuntimeError.

Цей метод активує щонайбільше n потоків, які очікують змінної умови; це безопераційний режим, якщо немає потоків, що очікують.

Поточна реалізація активує рівно n потоків, якщо принаймні n потоків очікують. Однак покладатися на таку поведінку небезпечно. Майбутня оптимізована реалізація може час від часу запускати більше ніж n потоків.

Примітка: пробуджений потік фактично не повертається після свого виклику wait(), доки він не зможе повторно отримати блокування. Оскільки notify() не знімає блокування, його виклик повинен це зробити.

notify_all()

Розбудити всі потоки, що очікують за цією умовою. Цей метод діє як notify(), але активує всі потоки, що очікують, а не один. Якщо потік, що викликає, не отримав блокування під час виклику цього методу, виникає RuntimeError.

Об’єкти семафора

Це один із найстаріших примітивів синхронізації в історії інформатики, винайдений першим голландським комп’ютерним науковцем Едсгером В. Дейкстрою (він використовував імена P() і V() замість acquire() і release()).

Семафор керує внутрішнім лічильником, який зменшується при кожному виклику acquire() і збільшується при кожному виклику release(). Лічильник ніколи не може опускатися нижче нуля; коли acquire() виявляє, що він дорівнює нулю, він блокується, чекаючи, поки інший потік викличе release().

Семафори також підтримують протокол управління контекстом.

class threading.Semaphore(value=1)

Цей клас реалізує семафорні об’єкти. Семафор керує атомарним лічильником, який представляє кількість викликів release() мінус кількість викликів acquire() плюс початкове значення. Метод acquire() блокує, якщо необхідно, доки не зможе повернутися, не зробивши лічильник негативним. Якщо не вказано, значення за замовчуванням дорівнює 1.

Необов’язковий аргумент дає початкове значення для внутрішнього лічильника; за замовчуванням 1. Якщо задане значення менше 0, виникає ValueError.

Змінено в версії 3.3: змінено з функції фабрики на клас.

acquire(blocking=True, timeout=None)

Придбайте семафор.

При виклику без аргументів:

  • Якщо внутрішній лічильник більше нуля під час входу, зменште його на одиницю та негайно поверніть True.

  • Якщо внутрішній лічильник дорівнює нулю під час входу, блокуйте, доки не пробудите виклик release(). Після пробудження (і лічильник більше 0), зменшіть лічильник на 1 і поверніть True. Кожен виклик release() пробуджуватиме рівно один потік. Не слід покладатися на порядок, у якому пробуджуються потоки.

When invoked with blocking set to false, do not block. If a call without an argument would block, return False immediately; otherwise, do the same thing as when called without arguments, and return True.

При виклику з timeout, відмінним від None, він блокуватиметься щонайбільше на timeout секунд. Якщо отримання не завершується успішно протягом цього інтервалу, повертається False. В іншому випадку поверніть True.

Змінено в версії 3.2: Параметр timeout є новим.

release(n=1)

Відпустіть семафор, збільшивши внутрішній лічильник на n. Коли він був нульовим під час входу, а інші потоки чекають, коли він знову стане більшим за нуль, розбудіть n з цих потоків.

Змінено в версії 3.9: Додано параметр n, щоб одночасно звільнити кілька потоків, що очікують.

class threading.BoundedSemaphore(value=1)

Клас, що реалізує обмежені семафорні об’єкти. Обмежений семафор перевіряє, чи його поточне значення не перевищує початкове значення. Якщо це так, виникає ValueError. У більшості ситуацій семафори використовуються для захисту ресурсів з обмеженою ємністю. Якщо семафор запускається занадто багато разів, це ознака помилки. Якщо не вказано, значення за замовчуванням дорівнює 1.

Змінено в версії 3.3: змінено з функції фабрики на клас.

Semaphore Приклад

Семафори часто використовуються для захисту ресурсів з обмеженою потужністю, наприклад, сервера бази даних. У будь-якій ситуації, коли розмір ресурсу фіксований, ви повинні використовувати обмежений семафор. Перед створенням будь-яких робочих потоків ваш головний потік ініціалізує семафор:

maxconnections = 5
# ...
pool_sema = BoundedSemaphore(value=maxconnections)

Після створення робочі потоки викликають методи отримання та випуску семафора, коли їм потрібно підключитися до сервера:

with pool_sema:
    conn = connectdb()
    try:
        # ... use connection ...
    finally:
        conn.close()

Використання обмеженого семафора зменшує ймовірність того, що програмна помилка, через яку семафор буде звільнено більше, ніж отримано, залишиться непоміченою.

Об’єкти подій

Це один із найпростіших механізмів зв’язку між потоками: один потік сигналізує про подію, а інші потоки чекають її.

Об’єкт події керує внутрішнім прапором, який можна встановити на true за допомогою методу set() і скинути на false за допомогою методу clear(). Метод wait() блокує, доки прапор не стане істинним.

class threading.Event

Клас, що реалізує об’єкти подій. Подія керує прапором, який можна встановити на true за допомогою методу set() і скинути на false за допомогою методу clear(). Метод wait() блокує, доки прапор не стане істинним. Прапор спочатку фальшивий.

Змінено в версії 3.3: змінено з функції фабрики на клас.

is_set()

Повертає True тоді і тільки тоді, коли внутрішній прапор має значення true.

set()

Встановіть для внутрішнього прапора значення true. Усі нитки, які чекають, коли це стане правдою, пробуджуються. Потоки, які викликають wait(), коли прапор встановлено як true, взагалі не блокуватимуться.

clear()

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

wait(timeout=None)

Block until the internal flag is true. If the internal flag is true on entry, return immediately. Otherwise, block until another thread calls set() to set the flag to true, or until the optional timeout occurs.

When the timeout argument is present and not None, it should be a floating point number specifying a timeout for the operation in seconds (or fractions thereof).

This method returns True if and only if the internal flag has been set to true, either before the wait call or after the wait starts, so it will always return True except if a timeout is given and the operation times out.

Змінено в версії 3.1: Раніше метод завжди повертав None.

Об’єкти таймера

Цей клас представляє дію, яку слід запускати лише після того, як мине певний час — таймер. Timer є підкласом Thread і як такий також функціонує як приклад створення власних потоків.

Timers are started, as with threads, by calling their start() method. The timer can be stopped (before its action has begun) by calling the cancel() method. The interval the timer will wait before executing its action may not be exactly the same as the interval specified by the user.

Наприклад:

def hello():
    print("hello, world")

t = Timer(30.0, hello)
t.start()  # after 30 seconds, "hello, world" will be printed
class threading.Timer(interval, function, args=None, kwargs=None)

Створіть таймер, який запускатиме функцію з аргументами args і аргументами ключового слова kwargs після закінчення інтервалу секунд. Якщо args має значення None (за замовчуванням), тоді використовуватиметься порожній список. Якщо kwargs має значення None (за замовчуванням), тоді використовуватиметься порожній dict.

Змінено в версії 3.3: змінено з функції фабрики на клас.

cancel()

Зупиніть таймер і скасуйте виконання дії таймера. Це спрацює, лише якщо таймер все ще перебуває в стадії очікування.

Загороджувальні об’єкти

Нове в версії 3.2.

Цей клас забезпечує простий примітив синхронізації для використання фіксованою кількістю потоків, які повинні чекати один одного. Кожен із потоків намагається подолати бар’єр, викликаючи метод wait() і блокуватиметься, доки всі потоки не виконають свої виклики wait(). У цей момент потоки звільняються одночасно.

Бар’єр можна повторно використовувати будь-яку кількість разів для однакової кількості потоків.

Як приклад, ось простий спосіб синхронізації потоку клієнта та сервера:

b = Barrier(2, timeout=5)

def server():
    start_server()
    b.wait()
    while True:
        connection = accept_connection()
        process_server_connection(connection)

def client():
    b.wait()
    while True:
        connection = make_connection()
        process_client_connection(connection)
class threading.Barrier(parties, action=None, timeout=None)

Створіть бар’єрний об’єкт для сторон кількості потоків. Action, якщо надається, є викликом, який викликається одним із потоків, коли вони звільняються. timeout — це значення часу очікування за замовчуванням, якщо для методу wait() його не вказано.

wait(timeout=None)

Пройти шлагбаум. Коли всі потоки, які є стороною бар’єру, викличуть цю функцію, усі вони звільняються одночасно. Якщо надається тайм-аут, він використовується замість будь-якого, який було надано конструктору класу.

Повернене значення є цілим числом у діапазоні від 0 до parties – 1, різне для кожного потоку. Це можна використати для вибору потоку для виконання деяких спеціальних завдань, наприклад:

i = barrier.wait()
if i == 0:
    # Only one thread needs to print this
    print("passed the barrier")

Якщо дія була надана конструктору, один із потоків викликатиме її до того, як буде звільнено. Якщо цей виклик викликає помилку, шлагбаум переходить у несправний стан.

Якщо виклик закінчився, шлагбаум переходить у несправний стан.

Цей метод може викликати виключення BrokenBarrierError, якщо бар’єр зламано або скинуто під час очікування потоку.

reset()

Поверніть бар’єр до стандартного порожнього стану. Будь-які потоки, які очікують на нього, отримають виняток BrokenBarrierError.

Зауважте, що використання цієї функції може потребувати певної зовнішньої синхронізації, якщо існують інші потоки, стан яких невідомий. Якщо бар’єр зламано, можливо, краще залишити його та створити новий.

abort()

Переведіть шлагбаум в зламаний стан. Це призводить до того, що будь-які активні або майбутні виклики wait() завершуються помилкою з BrokenBarrierError. Використовуйте це, наприклад, якщо один із потоків потрібно перервати, щоб уникнути блокування програми.

Можливо, буде краще просто створити бар’єр із розумним значенням timeout, щоб автоматично захистити один із потоків від збою.

parties

Кількість потоків, необхідних для проходження бар’єру.

n_waiting

Кількість потоків, які зараз очікують у бар’єрі.

broken

Логічне значення, яке має значення True, якщо бар’єр знаходиться в зламаному стані.

exception threading.BrokenBarrierError

Цей виняток, підклас RuntimeError, виникає, коли об’єкт Barrier скидається або зламано.

Використання блокувань, умов і семафорів у операторі with

All of the objects provided by this module that have acquire() and release() methods can be used as context managers for a with statement. The acquire() method will be called when the block is entered, and release() will be called when the block is exited. Hence, the following snippet:

with some_lock:
    # do something...

еквівалентно:

some_lock.acquire()
try:
    # do something...
finally:
    some_lock.release()

Наразі об’єкти Lock, RLock, Condition, Semaphore і BoundedSemaphore можна використовувати як контекст оператора with менеджери.