Appearance
Thread Class - Потоковый класс
Класс Thread
в Python является частью модуля threading
и представляет собой поток выполнения. Потоки позволяют выполнять несколько задач параллельно, что может повысить производительность программы.
Из официальной документации:
python
Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
Описание параметров:
Параметр | Описание (англ.) | Описание (рус.) |
---|---|---|
group | If not None, thread is a daemon. If a thread is a member of a target thread’s group, the remainder of its initialization is done in the other thread. | Если не None, поток является демоном. Если поток является членом группы целевого потока, остальная часть его инициализации выполняется в другом потоке. |
target | Callable object invoked by run() method. Defaults to None, meaning nothing is called. | Вызываемый объект, вызываемый методом run() . По умолчанию None , что означает, что ничего не вызывается. |
name | Thread name. By default, a unique name is constructed of the form “Thread-N” where N is a small decimal number. | Имя потока. По умолчанию, уникальное имя формируется в виде “Thread-N”, где N - небольшое десятичное число. |
args | Argument tuple for the target invocation. Defaults to ().__ | Кортеж аргументов для вызова целевого объекта. По умолчанию () . |
kwargs | Keyword argument dictionary for the target invocation. Defaults to {}. | Словарь ключевых аргументов для вызова целевого объекта. По умолчанию {} . |
daemon | A boolean indicating whether this thread is a daemon thread (True) or not (False). If None (the default), the daemon status is inherited from the current thread. | Булевое значение, указывающее, является ли этот поток демон-потоком (True) или нет (False). Если None (по умолчанию), статус демона наследуется от текущего потока. |
Описание методов класса Thread
(из документации):
Метод | Описание (англ.) | Описание (рус.) |
---|---|---|
start() | Start thread’s activity. It must be called at most once per thread object. | Запускает деятельность потока. Должен вызываться не более одного раза для каждого объекта потока. |
run() | Method representing the thread’s activity. You may override this method in a subclass. | Метод, представляющий деятельность потока. Вы можете переопределить этот метод в подклассе. |
join([timeout]) | Wait until thread terminates. | Ожидает до тех пор, пока поток не завершится. |
is_alive() | Return whether the thread is alive. | Возвращает состояние активности потока. |
getName() | Return thread's name. | Возвращает имя потока. |
setName(name) | Set thread's name. | Устанавливает имя потока. |
isDaemon() | Return whether the thread is a daemon thread. | Вернуть True, если поток является демоном. |
setDaemon(daemonic) | Set whether the thread is a daemon thread. | Установить, является ли поток демоном. |
Реализация класса наследника Thread
Для создания собственного класса, наследуемого от Thread
, необходимо импортировать модуль threading
и создать класс, который будет наследовать Thread
. В этом классе нужно переопределить метод run()
, который будет содержать код, выполняемый в потоке.
python
from threading import Thread
import time
class CustomThread(Thread):
def __init__(self, name, delay):
super().__init__()
self.name = name
self.delay = delay
def run(self):
print(f"Thread {self.name} started")
time.sleep(self.delay)
print(f"Thread {self.name} finished")
thread1 = CustomThread("Thread-1", 2)
thread2 = CustomThread("Thread-2", 4)
thread1.start()
thread2.start()
В этом примере создали класс CustomThread
, наследуемый от Thread
. В методе run()
выполняем необходимую логику, выводим сообщения о запуске и завершении потока.
Давайте разберем создание наследуемого от Thread
класса в Python и работу с его методами, включая передачу параметров, timeout
и join()
.
Создание наследуемого класса и передача параметров
Для создания класса, наследуемого от Thread
, нужно определить класс и переопределить метод run()
. Параметры target
, name
, args
, kwargs
, daemon
можно передать в конструктор родительского класса Thread
.
python
from threading import Thread
import time
class CustomThread(Thread):
def __init__(self, target=None, name=None, args=(), kwargs=None, daemon=None):
super().__init__(target=target, name=name, args=args, kwargs=kwargs, daemon=daemon)
self.result = None
def run(self):
if self.target:
self.result = self.target(*self.args, **self.kwargs)
def my_function(a, b, c=1):
time.sleep(2) # Имитация длительной операции
return a + b + c
# Пример использования:
my_thread = CustomThread(target=my_function, args=(1, 2), kwargs={'c': 3}, name="MyThread")
my_thread.start()
# Дополнительные действия, пока поток работает в фоне (благодаря daemon=True)
print("Главный поток продолжает выполнение...")
# Если нужно дождаться завершения потока, несмотря на daemon=True:
my_thread.join()
print(f"Результат выполнения потока: {my_thread.result}")
Поток с внешней функцией против класса наследника Thread
python
from threading import Thread
import time
# Подход с внешней функцией
def external_function(name, delay):
print(f"Thread {name} started")
time.sleep(delay)
print(f"Thread {name} finished")
thread = Thread(target=external_function, args=("Thread-1", 2))
thread.start()
# Подход с наследованием
class CustomThread(Thread):
def __init__(self, name, delay):
super().__init__()
self.name = name
self.delay = delay
def run(self):
print(f"Thread {self.name} started")
time.sleep(self.delay)
print(f"Thread {self.name} finished")
thread = CustomThread("Thread-2", 2)
thread.start()
Преимущества и недостатки класса наследника Thread против Thread класса с внешней функцией
Характеристика | Наследование Thread | Использование внешней функции |
---|---|---|
Организация кода | Лучше для сложной логики, сохраняет связанный код вместе | Проще для простых задач |
Совместное использование данных | Легче доступ к переменным экземпляра | Требуется передача данных в качестве аргументов |
Повторное использование | Более повторно используемый и расширяемый | Менее повторно используемый, особенно при различных потребностях в данных |
Сложность | Немного сложнее настроить | Легче понять для начинающих |
Сравнение работы обычного непоточного класса и класса наследника Thread
В первом примере создали обычный непоточный класс NonThreadedClass
. Деятельность объектов этого класса выполняется последовательно, что означает, что следующий объект не запустится, пока предыдущий не завершится.
python
import time
class NonThreadedClass:
def __init__(self, name):
self.name = name
def run(self):
print(f"Объект {self.name} запущен")
time.sleep(2)
print(f"Объект {self.name} завершен")
non_thread_1 = NonThreadedClass("Object-1")
non_thread_2 = NonThreadedClass("Object-2")
non_thread_3 = NonThreadedClass("Object-3")
non_thread_1.run()
non_thread_2.run()
non_thread_3.run()
Во втором примере создали класс ThreadedClass
, наследуемый от Thread
. Деятельность потоков этого класса выполняется параллельно, что означает, что все потоки запускаются одновременно и выполняются независимо друг от друга.
python
import threading
import time
class ThreadedClass(threading.Thread):
def __init__(self, name):
threading.Thread.__init__(self)
self.name = name
def run(self):
print(f"Поток {self.name} запущен")
time.sleep(2)
print(f"Поток {self.name} завершен")
thread1 = ThreadedClass("Thread-1")
thread2 = ThreadedClass("Thread-2")
thread3 = ThreadedClass("Thread-3")
thread1.start()
thread2.start()
thread3.start()
thread1.join()
thread2.join()
thread3.join()
В результате использование потоков позволяет повысить производительность программы, поскольку деятельность потоков может выполняться параллельно, а не последовательно.
Преимущества использования потоков через наследование:
- Лучшая организация кода
- Возможность хранения состояния
- Возможность расширения функционала
- Более удобное управление потоком
Упражнения
Есть функция, которая выводит квадраты чисел. Перепишите её в виде класса-наследника
Thread
так, чтобы каждый вывод квадрата выполнялся в отдельном потоке.pythondef print_squares(numbers): for n in numbers: print(f"Square of {n}: {n**2}") time.sleep(1)
Имеется код, где создаются несколько потоков с функцией, которая суммирует элементы списка. Перепишите этот код так, чтобы каждый поток был объектом класса-наследника
Thread
.pythondef sum_list(lst): print(f"Сумма списка {lst}: {sum(lst)}") lists = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] threads = [] for lst in lists: thread = threading.Thread(target=sum_list, args=(lst,)) threads.append(thread) thread.start() for thread in threads: thread.join()
Работа со строкой. Дополнить метод
run()
для подсчета гласных букв.pythonclass StringCounter(Thread): def __init__(self, text): Thread.__init__(self) self.text = text
Работа со списком. Дополнить метод
run()
для нахождения среднего значения.pythonclass ListProcessor(Thread): def __init__(self, numbers): Thread.__init__(self) self.numbers = numbers
Дополнить класс методами для чтения файла и подсчета строк
pythonclass FileReader(Thread): def __init__(self, filename): Thread.__init__(self) self.filename = filename
Функция, которая печатает числа от 1 до
n
с задержкойdelay
между каждым числом.Создайте класс-поток, который принимает строку и переводит ее в верхний регистр.
Создайте класс-поток, который имитирует работу таймера, выводящего текущее время каждую секунду.
Создайте класс-наследник
Thread
, который принимает строку и выводит её символы по одному в отдельном потоке. Каждый символ строки может выводиться с задержкой, чтобы имитировать работу потока.Создайте класс-наследник
Thread
, который принимает список чисел и выводит их квадраты в отдельном потоке. Запуск каждого потока должен происходить параллельно.Создайте класс-наследник
Thread
, который запускает несколько потоков для выполнения различных математических операций (например, умножение, деление и сложение) над числами.Допустим, у вас есть несколько чисел, и вы хотите запустить три различных потока для выполнения трех операций:
- Один поток умножает числа.
- Второй поток делит числа.
- Третий поток складывает числа. Запуск этих потоков должен происходить параллельно, а результат можно выводить по завершению каждого потока.