Сообщить об ошибке.

Класс Thread() модуля threading в Python

Создание потока thread и запуск вызываемого объекта в отдельном потоке

Синтаксис:

import threading

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

Параметры:

Конструктор threading.Thread() всегда следует вызывать с ключевыми аргументами.

  • group=None - зарезервировано для будущего расширения при реализации класса ThreadGroup.
  • target=None - вызываемый объект (функция), который будет вызываться методом Thread.run(). По умолчанию None и означает, что ничего не вызывается.
  • name=None - имя потока. По умолчанию уникальное имя создается в форме "Thread-N", где N - небольшое десятичное число или с версии Python 3.10 Thread-N (target), где target - это target.__ name__, если конечно указан аргумент target.
  • args=() - кортеж аргументов для вызываемого объекта target. По умолчанию ()
  • kwargs={} - словарь ключевых аргументов для вызываемого объекта target. По умолчанию {}.
  • daemon=None - если аргумент daemon не None, то он явно устанавливает, является ли поток демоническим. Если daemon=None (по умолчанию), то демоническое свойство наследуется от текущего потока.

Если подкласс переопределяет конструктор, то он должен обязательно вызвать конструктор базового класса (Thread.__init__()), прежде чем делать что-либо еще с потоком.

Возвращаемое значение:

Описание:

Класс Thread модуля threading запускает какое либо действие, которое будет выполняется в отдельном потоке управления.

Есть два способа запустить какое либо действие:

  1. Передать вызываемый объект (функцию) в конструктор.
  2. Переопределить метод Thread.run() в подклассе.

Внимание! Никакие другие методы не должны переопределяться в подклассе (кроме конструктора). Другими словами, можно переопределять только методы __init__() и Thread.run() этого класса.

Как только объект потока создан, его деятельность должна быть запущена путем вызова метода потока Thread.start(). Это вызывает метод Thread.run() в отдельном потоке управления.

Как только активность потока запущена, он считается "живым". Поток перестает быть активным, когда его метод Thread.run() завершается либо обычно, либо при возникновении необработанного исключения. Метод Thread.is_alive() проверяет, жив ли поток.

Другие потоки могут вызывать метод Thread.join(), который блокирует вызывающий поток до тех пор, пока не завершится поток, чей метод .join() вызван. Например, если для всех порожденных программой потоков вызвать этот метод, то дальнейшее выполнение программы будет заблокировано до тех пор пока все потоки не завершатся.

У потока есть имя. Имя может быть передано конструктору (аргумент name) и прочитано или изменено через атрибут Thread.name.

Если метод Thread.run() вызывает исключение, то для его обработки вызывается threading.excepthook(). По умолчанию threading.excepthook() молча игнорирует исключение SystemExit.

Поток можно пометить как "демонический поток". Значение этого флага заключается в том, что когда программа Python завершается, работающими остаются только потоки демона. Начальное значение наследуется от создающего потока. Флаг можно установить с помощью свойства Thread.daemon или аргумента конструктора daemon.

Примечание. Потоки демона внезапно останавливаются при завершении работы. Их ресурсы (такие как открытые файлы, транзакции базы данных и т. д.) могут быть освобождены неправильно. Если необходимо, чтобы потоки корректно останавливались, то делайте их недемоническими и используйте подходящий механизм сигнализации, такой как объект threading.Event().

Существует объект основного потока программы. Основной поток соответствует начальному потоку управления в программе Python. Это не поток демона.

Существует вероятность того, что будут созданы "объекты фиктивного потока". Это объекты потоков, соответствующие "чужеродным потокам", которые представляют собой потоки управления, запускаемые вне модуля потоковой передачи, например непосредственно из кода языка C. Объекты фиктивного потока имеют ограниченную функциональность. Они всегда считаются живыми и демоническими и не могут быть объединены методом Thread.join(). Они никогда не удаляются, так как невозможно обнаружить завершение чужих потоков.

Изменено в версии 3.10: если аргумент name опущен, то используется имя функции target.


Атрибуты и методы объекта Thread.


Thread.start():

Метод Thread.start() запускает экземпляр Thread в отдельном потоке управления.

Метод Thread.start() должен вызываться не более одного раза для каждого объекта потока. Он обеспечивает вызов метода Thread.run() объекта в отдельном потоке управления.

Метод вызовет исключение RuntimeError, если будет вызван более одного раза для одного и того же объекта потока.

import threading, time

def worker(num_thread):
    print(f'Старт потока №{num_thread}')
    time.sleep(1)
    print(f'Завершение работы потока №{num_thread}')

for i in range(2):
    # создаем экземпляры 'Thread' с функцией
    # 'worker()', которая запустится в отдельных 
    # трех потоках. Позиционные аргументы для 
    # функции 'worker()' передаются в кортеже `args`
    thread = threading.Thread(target=worker, args=(i,))
    # запускаем экземпляр `thread`
    thread.start()
    
# Старт потока №0
# Старт потока №1
# Завершение работы потока №0
# Завершение работы потока №1

Thread.run():

Метод Thread.run() представляет активность потока.

Стандартный метод Thread.run() вызывает вызываемый объект (функцию), переданный конструктору объекта в качестве целевого аргумента target, если таковой имеется, с позиционными аргументами args и ключевыми аргументами kwargs.

Метод Thread.run() можно переопределить в пользовательском подклассе. Например:

import threading, time

class Worker(threading.Thread):
    
    def __init__(self, num_thread):
        # вызываем конструктор базового класса
        super().__init__()
        # определяем аргументы собственного класса
        self.num_thread = num_thread
        
    def run(self):
        # теперь код функции 'worker()', которая должна 
        # выполняться в отдельных потоках будет здесь
        print(f'Старт потока №{self.num_thread}')
        time.sleep(1)
        print(f'Завершение работы потока №{self.num_thread}')

for i in range(2):
    # Создаем экземпляры класса 'Worker()'
    th = Worker(i)
    # запускаем потоки
    th.start()
    
# Старт потока №0
# Старт потока №1
# Завершение работы потока №0
# Завершение работы потока №1

Thread.join(timeout=None):

Метод Thread.join() ждет, пока поток не завершится. Этот метод блокирует вызывающий поток до тех пор, пока поток, чей метод .join() вызывается, не завершится - либо обычно, либо через необработанное исключение, либо пока не наступит необязательный тайм-аут timeout.

Когда присутствует аргумент timeout не равный None, то это должно быть число с плавающей запятой, указывающее тайм-аут для операции в секундах или его долях. Поскольку метод Thread.join()всегда возвращает None, то для определения жив ли поток или время ожидания вызова Thread.join() истекло, необходимо вызвать метод Thread.is_alive() после .join()

Когда аргумент тайм-аута отсутствует или timeout=None, то операция будет заблокирована до завершения потока.

Поток можно присоединять много раз.

Метод Thread.join() вызывает исключение RuntimeError, если предпринимается попытка присоединиться к текущему потоку (самому к себе), поскольку это может вызвать взаимоблокировку. Также ошибкой является присоединения потока до того, как он был запущен методом Thread.start(), а попытки сделать это - вызывают то же исключение.

import threading, time

def worker(i):
    n = i + 1
    print(f'Запуск потока №{n}')
    time.sleep(2)
    print(f'Поток №{n} выполнился.')
    
for i in range(2):
    thread = threading.Thread(target=worker, args=(i,))
    thread.start()
    # если присоединять 'thread.join()' потоки здесь, 
    # то они будут запускаться по очереди, т.к. 
    # основной поток программы будет ждать конца
    # выполнения присоединенного потока, прежде 
    # чем запустить следующий

print('Потоки запущены, основной поток программы так же выполняется')

# получаем экземпляр основного потока
main_thread = threading.main_thread()

# объединим потоки, что бы дождаться их выполнения
for t in threading.enumerate():
    # Список 'threading.enumerate()' включает в себя основной 
    # поток и т.к. присоединение основного потока самого к себе 
    # вызывает взаимоблокировку, то его необходимо пропустить
    if t is main_thread:
        continue
    print(f'Ожидание выполнения потока {t.name}')
    t.join()
    
print('Основной поток программы после ожидания продолжает работу')

# Запуск потока №1
# Запуск потока №2
# Потоки запущены, основной поток программы так же выполняется
# Ожидание выполнения потока Thread-1
# Поток №2 выполнился.
# Поток №1 выполнился.
# Ожидание выполнения потока Thread-2
# Основной поток программы после ожидания продолжает работу

Смотрите "Общий пример создания потоков", что бы узнать как проще объединять потоки.

Thread.name:

Атрибут Thread.name это строка, используется только для идентификации потока. У нее нет какой либо семантики. Несколько потоков могут иметь одно и то же имя. Начальное имя задается в конструкторе аргументом name.

import threading, time

def worker():
    # получаем экземпляр Thread и атрибутом
    # .name извлекаем имя потока
    thread_name = threading.current_thread().name
    print(f'Старт {thread_name}')
    time.sleep(1)

th1 = threading.Thread(target=worker)
# присвоение имени потока через атрибут
th1.name = 'FirstWorker'
th1.start()
# присвоение имени потока в конструкторе
th2 = threading.Thread(name='SecondWorker', target=worker)
th2.start()

# Старт FirstWorker
# Старт SecondWorker

Thread.setName(),
Thread.getName():

Методы Thread.setName() и Thread.getName() представляют собой старый API getter/setter для имени потока. Вместо этих методов используйте непосредственно атрибут Thread.name как свойство класса.

Устарел и не рекомендуется использовать, начиная с версии Python 3.10.

Thread.ident:

Атрибут Thread.ident идентификатор потока или None, если поток не был запущен. Это ненулевое целое число. Подробнее смотрите функцию threading.get_ident().

Идентификаторы потока могут быть повторно использованы при выходе из потока и создании другого потока. Идентификатор доступен даже после выхода из потока.

Thread.native_id:

Атрибут Thread.native_id собственный интегральный идентификатор потока. Это неотрицательное целое число или None, если поток не был запущен. Подробнее смотрите функцию threading.get_native_id().

Атрибут Thread.native_id представляет собой идентификатор потока (TID), присвоенный потоку ОС (ядром). Его значение может использоваться для однозначной идентификации этого конкретного потока в масштабах всей системы до завершения потока, после чего значение может быть переработано ОС.

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

Доступность: требуется функция threading.get_native_id().

Новое в Python 3.8.

Thread.is_alive():

Метод Thread.is_alive() сообщает, жив ли поток.

Этот метод возвращает True непосредственно перед запуском метода Thread.run() до тех пор, пока метод Thread.run() не завершится.

Функция модуля threading.enumerate() возвращает список всех живых потоков.

Thread.daemon:

Атрибут Thread.daemon представляет собой логическое значение, указывающее, является ли этот поток, потоком демона (True) или нет (False).

Атрибут Thread.daemon должен быть установлен до вызова метода Thread.start(), в противном случае возникает исключение RuntimeError.

Его начальное значение наследуется от создающего потока. Основной поток не является потоком демона и поэтому все потоки, созданные в основном потоке, по умолчанию имеют значение daemon = False.

Вся программа Python завершается, когда не остается живых потоков, не являющихся демонами.

Устанавливается и извлекается так же, как атрибут Thread.name, только имеет значение bool.

Thread.setDaemon(),
Thread.isDaemon():

Методы Thread.setDaemon() и Thread.isDaemon() представляют собой старый API для getter/setter для атрибута daemon. Вместо этих методов используйте атрибут Thread.daemon напрямую как свойство.

Устарел и не рекомендуется использовать, начиная с версии Python 3.10.


Общий пример создания потоков классом Thread.

import threading


def worker(i):
    """thread worker function"""
    print(f'Worker-{i}')


threads = []
# запускаем функцию 'worker()' 
# для выполнения в 5-ти потоках
for i in range(5):
    t = threading.Thread(target=worker, args=(i,))
    threads.append(t)
    t.start()
    
# блокируем дальнейшее выполнение программы
# пока не закончат выполняться все 5 потоков
[thread.join() for thread in threads]