В этом посте мы формируем понимание многопоточности и разбираемся в ее реализации в Python.
Mutlithreading — способ достижения многозадачности
Мы используем потоки для достижения возможности многозадачности на компьютере, это требует от нас общего понимания того, что такое потоки.
И прежде чем понять, что такое поток, нам нужно знать процесс.
Процесс — это экземпляр компьютерной программы, который выполняется.
Каждый процесс имеет исполняемую программу, связанные данные, контекст выполнения (состояние процесса).
Поток — это наименьшая сущность в процессе, выполнение которой может быть запланировано. Это набор инструкций, которые можно выполнять независимо от другого кода в процессе.
По сути, я мог бы захотеть делать несколько вещей одновременно, чтобы сформировать правильную рабочую машину (процесс), которая является несколько независимыми частями (через потоки).
Каждый поток имеет определенную информацию в блоке управления потоком (TCB), который содержит идентификатор потока, указатель стека, счетчик программ, состояние потока, набор регистров потока, указатель родительского процесса.
В одном процессе может существовать несколько потоков, где:
- Каждый поток содержит свой собственный набор регистров и локальные переменные (хранящиеся в стеке).
- Все потоки процесса совместно используют глобальные переменные (хранящиеся в куче) и программный код.
Многопоточность определяется как способность процессора одновременно выполнять несколько потоков.
Таким образом, в основном в одноядерном процессоре он будет переключаться между несколькими потоками и сохранять состояние потока каждый раз, когда он останавливает один, чтобы запустить другой, когда есть ручное прерывание или прерывание ввода-вывода.
Пример многопоточности в Python
В этой части мы демонстрируем разницу между однопоточным и многопоточным процессами с двумя компонентами, т. е. вычислением квадрата и вычислением куба:
- Однопоточный процесс: время выполнения: 0,801 сек.
# This is an example of sequential programming # where components are executed one by one. # Imports import time # Components of a process # calc_square and calc_cube def calc_square(numbers): for n in numbers: print(f'\n{n} ^ 2 = {n*n}') time.sleep(0.1) def calc_cube(numbers): for n in numbers: print(f'\n{n} ^ 3 = {n*n*n}') time.sleep(0.1) numbers = [2, 3, 5, 8] start = time.time() # executing calc_square and calc_cube # one after another! calc_square(numbers) calc_cube(numbers) end = time.time() print('Execution Time: {}'.format(end-start))
- Многопоточный процесс: время выполнения: 0,402 сек.
# Imports import time import threading # Components of a process # calc_square and calc_cube def calc_square(numbers): for n in numbers: print(f'\n{n} ^ 2 = {n*n}') time.sleep(0.1) def calc_cube(numbers): for n in numbers: print(f'\n{n} ^ 3 = {n*n*n}') time.sleep(0.1) numbers = [2, 3, 5, 8] start = time.time() # executing calc_square and calc_cube # in different threads!! square_thread = threading.Thread(target=calc_square, args=(numbers,)) cube_thread = threading.Thread(target=calc_cube, args=(numbers,)) # starts execution of two threads! square_thread.start() cube_thread.start() square_thread.join() cube_thread.join() end = time.time() print('Execution Time: {}'.format(end-start))
В многопоточном процессе мы вдвое сокращаем время, затрачиваемое программой на выполнение!! Многопоточность позволяет нам одновременно выполнять квадратные и кубические потоки.
Мы используем .start(), чтобы начать выполнение потока, и используем .join(), чтобы сообщить, что говорит одному потоку дождаться завершения другого.
Он выполняет функцию calc_cube(), в то время как метод sleep приостанавливает выполнение calc_square() на 0,1 секунды, затем переходит в спящий режим и выполняет функцию calc_square(). По сути, операционная система переключается между потоками, запуская каждый из них понемногу, что улучшает время выполнения процесса.
Подробнее здесь: