В этом посте мы формируем понимание многопоточности и разбираемся в ее реализации в 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(). По сути, операционная система переключается между потоками, запуская каждый из них понемногу, что улучшает время выполнения процесса.

Подробнее здесь: