threading — Thread-based parallelism

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


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

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

Дивись також

concurrent.futures.ThreadPoolExecutor offers a higher level interface to push tasks to a background thread without blocking execution of the calling thread, while still being able to retrieve their results when needed.

queue provides a thread-safe interface for exchanging data between running threads.

asyncio offers an alternative approach to achieving task level concurrency without requiring the use of multiple operating system threads.

Примітка

У серії Python 2.x цей модуль містив назви camelCase для деяких методів і функцій. Вони застаріли з Python 3.10, але вони все ще підтримуються для сумісності з Python 2.5 і старішими.

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

Availability: not Emscripten, not WASI.

This module does not work or is not available on WebAssembly platforms wasm32-emscripten and wasm32-wasi. See WebAssembly platforms for more information.

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

threading.active_count()

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

Функція activeCount є застарілим псевдонімом для цієї функції.

threading.current_thread()

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

Функція currentThread є застарілим псевдонімом для цієї функції.

threading.excepthook(args, /)

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

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

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

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

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

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

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

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

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

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

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

Дивись також

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

Added in version 3.8.

threading.__excepthook__

Зберігає вихідне значення threading.excepthook(). Він зберігається, щоб можна було відновити початкове значення у випадку, якщо вони заміняться зламаними або альтернативними об’єктами.

Added in version 3.10.

threading.get_ident()

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

Added in version 3.3.

threading.get_native_id()

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

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

Added in version 3.8.

threading.enumerate()

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

threading.main_thread()

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

Added in version 3.4.

threading.settrace(func)

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

threading.settrace_all_threads(func)

Set a trace function for all threads started from the threading module and all Python threads that are currently executing.

The func will be passed to sys.settrace() for each thread, before its run() method is called.

Added in version 3.12.

threading.gettrace()

Отримайте функцію трасування, встановлену settrace().

Added in version 3.10.

threading.setprofile(func)

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

threading.setprofile_all_threads(func)

Set a profile function for all threads started from the threading module and all Python threads that are currently executing.

The func will be passed to sys.setprofile() for each thread, before its run() method is called.

Added in version 3.12.

threading.getprofile()

Отримайте функцію профайлера, встановлену setprofile().

Added in version 3.10.

threading.stack_size([size])

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

Availability: Windows, pthreads.

Unix platforms with POSIX threads support.

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

threading.TIMEOUT_MAX

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

Added in version 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: Lib/_threading_local.py.

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

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 joined. 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, тобто нічого не викликається.

ім’я – це ім’я потоку. За замовчуванням унікальне ім’я створюється у формі «Thread-N», де N — маленьке десяткове число, або «Thread-N (target)», де «target» — це target.__name__ якщо вказано аргумент target.

args is a list or tuple of arguments for the target invocation. Defaults to ().

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

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

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

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

Змінено в версії 3.10: Використовуйте назву ціль, якщо аргумент ім’я пропущено.

start()

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

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

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

run()

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

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

Using list or tuple as the args argument which passed to the Thread could achieve the same effect.

Приклад:

>>> from threading import Thread
>>> t = Thread(target=print, args=[1])
>>> t.run()
1
>>> t = Thread(target=print, args=(1,))
>>> t.run()
1
join(timeout=None)

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

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

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

A thread can be joined many times.

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

name

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

getName()
setName()

Застарілий API отримання/налаштування для name; використовуйте його безпосередньо як властивість.

Застаріло починаючи з версії 3.10.

ident

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

native_id

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

Примітка

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

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

Added in version 3.8.

is_alive()

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

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

daemon

Логічне значення, яке вказує, чи є цей потік потоком демона (True) чи ні (False). Це має бути встановлено перед викликом start(), інакше виникає RuntimeError. Його початкове значення успадковується від потоку створення; основний потік не є потоком демона, тому всі потоки, створені в основному потоці, за замовчуванням мають daemon = False.

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

isDaemon()
setDaemon()

Застарілий API отримання/налаштування для daemon; використовуйте його безпосередньо як властивість.

Застаріло починаючи з версії 3.10.

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

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

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

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

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

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

class threading.Lock

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

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

acquire(blocking=True, timeout=-1)

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

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

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

Під час виклику з плаваючою комою timeout аргументу встановлено додатне значення, блокувати щонайбільше на кількість секунд, визначену timeout, і до тих пір, поки блокування не може бути отримано. Аргумент timeout, рівний -1, визначає необмежену очікування. Заборонено вказувати тайм-аут, якщо блокування має значення False.

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

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

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

release()

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

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

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

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

locked()

Повертає True, якщо блокування отримано.

Об’єкти RLock

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

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

Повторне блокування також підтримує протокол керування контекстом.

class threading.RLock

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

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

acquire(blocking=True, timeout=-1)

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

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

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

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

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

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

release()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

class threading.Condition(lock=None)

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

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

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

acquire(*args)

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

release()

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

wait(timeout=None)

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

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

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

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

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

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

wait_for(predicate, timeout=None)

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

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

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

while not predicate():
    cv.wait()

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

Added in version 3.2.

notify(n=1)

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

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

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

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

notify_all()

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

Метод notifyAll є застарілим псевдонімом для цього методу.

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

Це один із найстаріших примітивів синхронізації в історії інформатики, винайдений першим голландським комп’ютерним науковцем Едсгером В. Дейкстрою (він використовував імена 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() пробуджуватиме рівно один потік. Не слід покладатися на порядок, у якому пробуджуються потоки.

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

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

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

release(n=1)

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

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

class threading.BoundedSemaphore(value=1)

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

Метод isSet є застарілим псевдонімом для цього методу.

set()

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

clear()

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

wait(timeout=None)

Block as long as the internal flag is false and the timeout, if given, has not expired. The return value represents the reason that this blocking method returned; True if returning because the internal flag is set to true, or False if a timeout is given and the the internal flag did not become true within the given wait time.

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.

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

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

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

Timers are started, as with threads, by calling their Timer.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()

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

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

Added in version 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 менеджери.