# `threading` — Thread-based parallelism

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

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

Availability: not WASI.

This module does not work or is not available on WebAssembly. See WebAssembly platforms for more information.

## Introduction

The `threading` module provides a way to run multiple threads (smaller units of a process) concurrently within a single process. It allows for the creation and management of threads, making it possible to execute tasks in parallel, sharing memory space. Threads are particularly useful when tasks are I/O bound, such as file operations or making network requests, where much of the time is spent waiting for external resources.

A typical use case for `threading` includes managing a pool of worker threads that can process multiple tasks concurrently. Here’s a basic example of creating and starting threads using `Thread`:

    import threading
    import time

    def crawl(link, delay=3):
        print(f"crawl started for {link}")
        time.sleep(delay)  # Blocking I/O (simulating a network request)
        print(f"crawl ended for {link}")

    links = [
        "https://python.org",
        "https://docs.python.org",
        "https://peps.python.org",
    ]

    # Start threads for each link
    threads = []
    for link in links:
        # Using `args` to pass positional arguments and `kwargs` for keyword arguments
        t = threading.Thread(target=crawl, args=(link,), kwargs={"delay": 2})
        threads.append(t)

    # Start each thread
    for t in threads:
        t.start()

    # Wait for all threads to finish
    for t in threads:
        t.join()

Змінено в версії 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, завдяки Global Interpreter Lock, лише один потік може виконувати код Python одночасно (навіть якщо певні бібліотеки, орієнтовані на продуктивність, можуть подолати це обмеження). Якщо ви хочете, щоб ваша програма краще використовувала обчислювальні ресурси багатоядерних машин, радимо використовувати `multiprocessing` або `concurrent.futures.ProcessPoolExecutor`. Однак потокова розв’язка все ще є відповідною моделлю, якщо ви хочете одночасно запускати кілька завдань, пов’язаних із вводом-виводом.

## GIL and performance considerations

Unlike the `multiprocessing` module, which uses separate processes to bypass the global interpreter lock (GIL), the threading module operates within a single process, meaning that all threads share the same memory space. However, the GIL limits the performance gains of threading when it comes to CPU-bound tasks, as only one thread can execute Python bytecode at a time. Despite this, threads remain a useful tool for achieving concurrency in many scenarios.

As of Python 3.13, free-threaded builds can disable the GIL, enabling true parallel execution of threads, but this feature is not available by default (see PEP 703).

## Reference

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

threading.active_count()

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

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

threading.current_thread()

  Return the current `Thread` object, corresponding to the caller’s thread of control. If the caller’s thread of control was not created through the `threading` module, a dummy thread object with limited functionality is returned.

  Функція `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, GNU/kFreeBSD, Solaris.

  Added in version 3.8.

  Змінено в версії 3.13: Added support for GNU/kFreeBSD.

  Змінено в версії 3.15: Added support for Solaris.

threading.enumerate()

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

threading.main_thread()

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

  Added in version 3.4.

threading.settrace(func)

  Set a trace function for all threads started from the `threading` module. The func will be passed to `sys.settrace()` for each thread, before its `run()` method is called.

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)

  Set a profile function for all threads started from the `threading` module. The func will be passed to `sys.setprofile()` for each thread, before its `run()` method is called.

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, коли вони реалізовані, відображаються на функції рівня модуля.

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

### Thread-local data

Thread-local data is data whose values are thread specific. If you have data that you want to be local to a thread, create a `local` object and use its attributes:

    >>> mydata = local()
    >>> mydata.number = 42
    >>> mydata.number
    42

You can also access the `local`-object’s dictionary:

    >>> mydata.__dict__
    {'number': 42}
    >>> mydata.__dict__.setdefault('widgets', [])
    []
    >>> mydata.widgets
    []

If we access the data in a different thread:

    >>> log = []
    >>> def f():
    ...     items = sorted(mydata.__dict__.items())
    ...     log.append(items)
    ...     mydata.number = 11
    ...     log.append(mydata.number)

    >>> import threading
    >>> thread = threading.Thread(target=f)
    >>> thread.start()
    >>> thread.join()
    >>> log
    [[], 11]

we get different data. Furthermore, changes made in the other thread don’t affect data seen in this thread:

    >>> mydata.number
    42

Of course, values you get from a `local` object, including their `__dict__` attribute, are for whatever thread was current at the time the attribute was read. For that reason, you generally don’t want to save these values across threads, as they apply only to the thread they came from.

You can create custom `local` objects by subclassing the `local` class:

    >>> class MyLocal(local):
    ...     number = 2
    ...     def __init__(self, /, **kw):
    ...         self.__dict__.update(kw)
    ...     def squared(self):
    ...         return self.number ** 2

This can be useful to support default values, methods and initialization. Note that if you define an `__init__()` method, it will be called each time the `local` object is used in a separate thread. This is necessary to initialize each thread’s dictionary.

Now if we create a `local` object:

    >>> mydata = MyLocal(color='red')

we have a default number:

    >>> mydata.number
    2

an initial color:

    >>> mydata.color
    'red'
    >>> del mydata.color

And a method that operates on the data:

    >>> mydata.squared()
    4

As before, we can access the data in a separate thread:

    >>> log = []
    >>> thread = threading.Thread(target=f)
    >>> thread.start()
    >>> thread.join()
    >>> log
    [[('color', 'red')], 11]

without affecting this thread’s data:

    >>> mydata.number
    2
    >>> mydata.color
    Traceback (most recent call last):
    ...
    AttributeError: 'MyLocal' object has no attribute 'color'

Note that subclasses can define __slots__, but they are not thread local. They are shared across threads:

    >>> class MyLocal(local):
    ...     __slots__ = 'number'

    >>> mydata = MyLocal()
    >>> mydata.number = 42
    >>> mydata.color = 'red'

So, the separate thread:

    >>> thread = threading.Thread(target=f)
    >>> thread.start()
    >>> thread.join()

affects what we see:

    >>> mydata.number
    11

class threading.local

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

### Thread objects

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, context=None)

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

  group must be `None` as it is 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` (за замовчуванням), демонічна властивість успадковується з поточного потоку.

  context is the `Context` value to use when starting the thread. The default value is `None` which indicates that the `sys.flags.thread_inherit_context` flag controls the behaviour. If the flag is true, threads will start with a copy of the context of the caller of `start()`. If false, they will start with an empty context. To explicitly start with an empty context, pass a new instance of `Context()`. To explicitly start with a copy of the current context, pass the value from `copy_context()`. The flag defaults true on free-threaded builds and false otherwise.

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

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

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

  Змінено в версії 3.14: Added the context parameter.

  start()

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

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

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

    If supported, set the operating system thread name to `threading.Thread.name`. The name can be truncated depending on the operating system thread name limits.

    Змінено в версії 3.14: Set the operating system thread name.

  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()` викликається, не завершиться (звичайно або через необроблений виняток), або доки не настане додатковий час очікування.

    When the timeout argument is present and not `None`, it should be a real 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 joined many times.

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

    If an attempt is made to join a running daemonic thread in late stages of Python finalization `join()` raises a `PythonFinalizationError`.

    Змінено в версії 3.14: May raise `PythonFinalizationError`.

    Змінено в версії 3.15: Accepts any real number as timeout, not only integer or float.

  name

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

    On some platforms, the thread name is set at the operating system level when the thread starts, so that it is visible in task managers. This name may be truncated to fit in a system-specific limit (for example, 15 bytes on Linux or 63 bytes on macOS).

    Changes to name are only reflected at the OS level when the currently running thread is renamed. (Setting the name attribute of a different thread only updates the Python Thread object.)

  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.

### Lock objects

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

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

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

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

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

class threading.Lock

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

  Змінено в версії 3.13: `Lock` is now a class. In earlier Pythons, `Lock` was a factory function which returned an instance of the underlying private lock type.

  acquire(blocking=True, timeout=-1)

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

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

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

    When invoked with the 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, якщо це підтримує базова реалізація потоків.

    Змінено в версії 3.14: Lock acquisition can now be interrupted by signals on Windows.

    Змінено в версії 3.15: Accepts any real number as timeout, not only integer or float.

  release()

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

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

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

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

  locked()

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

### RLock objects

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

Threads call a lock’s `acquire()` method to lock it, and its `release()` method to unlock it.

Примітка

Reentrant locks support the context management protocol, so it is recommended to use `with` instead of manually calling `acquire()` and `release()` to handle acquiring and releasing the lock for a block of code.

RLock’s `acquire()`/`release()` call pairs may be nested, unlike Lock’s `acquire()`/`release()`. Only the final `release()` (the `release()` of the outermost pair) resets the lock to an unlocked state and allows another thread blocked in `acquire()` to proceed.

`acquire()`/`release()` must be used in pairs: each acquire must have a release in the thread that has acquired the lock. Failing to call release as many times the lock has been acquired can lead to deadlock.

class threading.RLock

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

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

  acquire(blocking=True, timeout=-1)

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

    Дивись також

    Using RLock as a context manager

      Recommended over manual `acquire()` and `release()` calls whenever practical.

    When invoked with the blocking argument set to `True` (the default):

    > - If no thread owns the lock, acquire the lock and return immediately.

    > - If another thread owns the lock, block until we are able to acquire lock, or timeout, if set to a positive value.

    > - If the same thread owns the lock, acquire the lock again, and return immediately. This is the difference between `Lock` and `RLock`; `Lock` handles this case the same as the previous, blocking until the lock can be acquired.

    When invoked with the blocking argument set to `False`:

    > - If no thread owns the lock, acquire the lock and return immediately.

    > - If another thread owns the lock, return immediately.

    > - If the same thread owns the lock, acquire the lock again and return immediately.

    In all cases, if the thread was able to acquire the lock, return `True`. If the thread was unable to acquire the lock (i.e. if not blocking or the timeout was reached) return `False`.

    If called multiple times, failing to call `release()` as many times may lead to deadlock. Consider using `RLock` as a context manager rather than calling acquire/release directly.

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

    Змінено в версії 3.15: Accepts any real number as timeout, not only integer or float.

  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 not acquired.

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

  locked()

    Return a boolean indicating whether this object is locked right now.

    Added in version 3.14.

### Condition objects

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

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

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

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

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

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

    # 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()

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

  locked()

    Return a boolean indicating whether this object is locked right now.

    Added in version 3.14.

  wait(timeout=None)

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

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

    When the timeout argument is present and not `None`, it should be a real 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()`: блокування має бути утримано під час виклику та повторно отримане після повернення. Предикат обчислюється з утриманим блокуванням.

    Added in version 3.2.

  notify(n=1)

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

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

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

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

  notify_all()

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

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

### Semaphore objects

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

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

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

class threading.Semaphore(value=1)

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

    Змінено в версії 3.15: Accepts any real number as timeout, not only integer or float.

  release(n=1)

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

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

class threading.BoundedSemaphore(value=1)

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

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

### `Semaphore` example

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

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

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

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

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

### Event objects

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

Об’єкт події керує внутрішнім прапором, який можна встановити на 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 internal flag did not become true within the given wait time.

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

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

### Timer objects

Цей клас представляє дію, яку слід запускати лише після того, як мине певний час — таймер. `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()

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

### Barrier objects

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

## Iterator synchronization

By default, Python iterators do not support concurrent access. Most iterators make no guarantees when accessed simultaneously from multiple threads. Generator iterators, for example, raise `ValueError` if one of their iterator methods is called while the generator is already executing. The tools in this section allow reliable concurrency support to be added to ordinary iterators and iterator-producing callables.

The `serialize_iterator` wrapper lets multiple threads share a single iterator and take turns consuming from it. While one thread is running `__next__()`, the others block until the iterator becomes available. Each value produced by the underlying iterator is delivered to exactly one caller.

The `concurrent_tee()` function lets multiple threads each receive the full stream of values from one underlying iterator. It creates independent iterators that all draw from the same source. Values are buffered until consumed by all of the derived iterators.

class threading.serialize_iterator(iterable)

  Return an iterator wrapper that serializes concurrent calls to `__next__()` using a lock.

  If the wrapped iterator also defines `send()`, `throw()`, or `close()`, those calls are serialized as well.

  This makes it possible to share a single iterator, including a generator iterator, between multiple threads. A lock ensures that calls are handled one at a time. No values are duplicated or skipped by the wrapper itself. Each item from the underlying iterator is given to exactly one caller.

  This wrapper does not copy or buffer values. Threads that call `next()` while another thread is already advancing the iterator will block until the active call completes.

  Example:

      import threading

      def squares(n):
          for x in range(n):
              yield x * x

      def consume(name, iterable):
          for item in iterable:
              print(name, item)

      source = threading.serialize_iterator(squares(5))

      t1 = threading.Thread(target=consume, args=("left", source))
      t2 = threading.Thread(target=consume, args=("right", source))
      t1.start()
      t2.start()
      t1.join()
      t2.join()

  In this example, each number is printed exactly once, but the work is shared between the two threads.

  Added in version 3.15.

threading.synchronized_iterator(func)

  Wrap an iterator-producing callable so that each iterator it returns is automatically passed through `serialize_iterator`.

  This is especially useful as a decorator for generator functions, allowing their generator-iterators to be consumed from multiple threads.

  Example:

      import threading

      @threading.synchronized_iterator
      def squares(n):
          for x in range(n):
              yield x * x

      def consume(name, iterable):
          for item in iterable:
              print(name, item)

      source = squares(5)

      t1 = threading.Thread(target=consume, args=("left", source))
      t2 = threading.Thread(target=consume, args=("right", source))
      t1.start()
      t2.start()
      t1.join()
      t2.join()

  The returned wrapper preserves the metadata of func, such as its name and wrapped function reference.

  Added in version 3.15.

threading.concurrent_tee(iterable, n=2)

  Return n independent iterators from a single input iterable, with guaranteed behavior when the derived iterators are consumed concurrently.

  This function is similar to `itertools.tee()`, but is intended for cases where the source iterator may feed consumers running in different threads. Each returned iterator yields every value from the underlying iterable, in the same order.

  Internally, values are buffered until every derived iterator has consumed them.

  The returned iterators share the same underlying synchronization lock. Each individual derived iterator is intended to be consumed by one thread at a time. If a single derived iterator must itself be shared by multiple threads, wrap it with `serialize_iterator`.

  If n is `0`, return an empty tuple. If n is negative, raise `ValueError`.

  Example:

      import threading

      def squares(n):
          for x in range(n):
              yield x * x

      def consume(name, iterable):
          for item in iterable:
              print(name, item)

      source = squares(5)
      left, right = threading.concurrent_tee(source)

      t1 = threading.Thread(target=consume, args=("left", left))
      t2 = threading.Thread(target=consume, args=("right", right))
      t1.start()
      t2.start()
      t1.join()
      t2.join()

  In this example, both consumer threads see the full sequence of squares from a single generator expression.

  Added in version 3.15.