Python - работа с потоками

Python — работа с потоками

Привет всем! Сегодня — небольшой пост, посвященный вопросу «Работа с потоками в Python». Ну и конечно — пример кода для работы с потоками 🙂 Надеюсь — будет полезно.

Итак, что такое потоки в Python? Python использует потоки для реализации многозадачности. Потоки позволяют запускать несколько команд или задач одновременно и манипулировать ими, не останавливая выполнение других задач. ЗАчем это нужно? Ну, например — это может быть полезно, при работе с сетью или многомерными данными, где необходимо выполнять несколько операций одновременно и обрабатывать результаты параллельно.

Теперь пример работы с потоками. Будем выводить результат вычислений числа Фибоначчи (можно что-то другое, но сейчас в голову не приходит ничего больше из того, что может заставить компьютер сильно задуматься). Естественно — мы будем смотреть на время, в течении которого выполняется код. Для этого и нужна библиотека time. Итак, вывод числа Фибоначчи для, например, числа 40:

import time
 
def fibonacci(n):
if n <= 0:
return 0
elif n == 1:
return 1
else:
return fibonacci(n - 1) + fibonacci(n - 2)
 
start = time.time()
print('Результат: ', fibonacci(40))
end = time.time()
result = end - start
print('Начало работы: ', start)
print('Конец работы: ', end)
print('Код работал: ', result, 'секунд')

Получаем результат:

Результат:  102334155
Начало работы: 1674906951.9962122
Конец работы: 1674907029.9873629
Код работал: 77.99115061759949 секунд

Это для понимания того, что задача ого-го какая ресурсоемкая для компьютера…

Ок, а теперь изменим код так, что бы он считал числа Фибоначчи в промежутке от 1 до 30:

import time
 
def fibonacci(n):
if n <= 0:
return 0
elif n == 1:
return 1
else:
return fibonacci(n - 1) + fibonacci(n - 2)
 
start = time.time()
for i in range(1, 31):
print('Для числа', i, ':', fibonacci(i))
# print('результат: ', fibonacci(i))
end = time.time()
 
result = end - start
print('Код работал ', result, 'секунд')

После чего получаем результат:

Для числа 1 : 1
Для числа 2 : 1
Для числа 3 : 2
Для числа 4 : 3
Для числа 5 : 5
Для числа 6 : 8
Для числа 7 : 13
Для числа 8 : 21
Для числа 9 : 34
Для числа 10 : 55
Для числа 11 : 89
Для числа 12 : 144
Для числа 13 : 233
Для числа 14 : 377
Для числа 15 : 610
Для числа 16 : 987
Для числа 17 : 1597
Для числа 18 : 2584
Для числа 19 : 4181
Для числа 20 : 6765
Для числа 21 : 10946
Для числа 22 : 17711
Для числа 23 : 28657
Для числа 24 : 46368
Для числа 25 : 75025
Для числа 26 : 121393
Для числа 27 : 196418
Для числа 28 : 317811
Для числа 29 : 514229
Для числа 30 : 832040
Код работал  1.2446682453155518 секунд

Прекрасно! А теперь представим, что нам нужно (кто его знает, зачем, но нужно) — считать числа Фибоначчи одновременно. Если мы запустим выполнение задачи в цикле (например, 10 раз подряд) — т.е. считаем числа от 1 до 30 первый раз — то тратим 1,24 секунд. Считаем второй раз — опять 1,24 секунд. Каждый следующий проход увеличивает время на те же самые плюс-минус 1,24 секунд.
Посмотрите на код, который демонстрирует данную возможность:

import time
 
def fibonacci(n):
if n <= 0:
return 0
elif n == 1:
return 1
else:
return fibonacci(n - 1) + fibonacci(n - 2)
 
 
start = time.time()
 
for x in range(1, 11):
print('Проход: ', x)
for i in range(1, 31):
print('Для числа', i, ':', fibonacci(i))
 
end = time.time()
result = end - start
print('Код работал ', result, 'секунд')

И теперь результат:

Код работал  14.41745662689209 секунд

А что, если начать использовать потоки?

Давайте попробуем (комментарии к коду — внутри):

#Импортируем модули threading и time, которые будем использовать для создания и запуска потоков, а так #же для измерения времени выполнения.
import threading
import time
 
#Эта функция вычисляет число Фибоначчи для данного индекса n.
def fibonacci(n):
if n <= 0:
return 0
elif n == 1:
return 1
else:
return fibonacci(n - 1) + fibonacci(n - 2)
 
#Эта функция будет исполняться в каждом потоке. Выводит сообщение о том, что поток работает, затем #использует функцию fibonacci() для вычисления и вывода чисел Фибоначчи для индексов от 1 до 30.
def thread_func(i):
print(f'Поток {i} работает...')
for i in range(1, 31):
print('Для числа', i, ':', fibonacci(i))
 
#Запоминаем время начала работы
start = time.time()
 
#Создаем 10 потоков, каждый из которых будет исполнять функцию thread_func. Эта функция печатает #сообщение о том, что поток работает, а затем исполняет цикл от 1 до 30 и для каждого числа вызывает #функцию fibonacci, которая находит числа Фибоначчи для этого числа и выводит результат на экран. После #создания всех потоков, программа засекает время и выводит начальное и конечное время, а также разницу #между ними, чтобы определить, сколько времени заняла работа программы.
threads = []
for i in range(10):
t = threading.Thread(target=thread_func, args=(i,))
threads.append(t)
t.start()
 
for thread in threads:
thread.join()
 
end = time.time()
result = end - start
 
print('Начало: ', start)
print('Конец: ', end)
print('Код работал ', result, 'секунд')

Итак, код с потоками имеется, и он — после выполнения — показывает следующие результаты работы программы:

Код работал  12.041714906692505 секунд

что на 2 секунды быстрее, если бы мы повторяли цикл раз за разом (напомню, что выполнение одного цикла у нас занимало 14 с копейками секунд.

Вот… надеюсь, вам стало немного проще разобраться с потоками в Python и как с ними работать. Как всегда — в случае возникновения вопросов пишите в Telegram или на почту 😉