Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Мультитрединг
Мультитрединг — это механизм, при котором программа может выполнять несколько потоков выполнения (threads) одновременно, обеспечивая параллельное выполнение задач внутри одного процесса.
Основные концепции
Поток (Thread) — это легковесный процесс, который работает в контексте одного процесса и делит с другими потоками общую память, файловые дескрипторы и другие ресурсы. Это делает мультитрединг эффективнее мультипроцессинга для задач с интенсивным обменом данными.
Использование threading в Python
Модуль threading предоставляет удобный API для работы с потоками:
import threading
import time
def worker(name, delay):
time.sleep(delay)
print(f"{name} завершил работу")
# Создание и запуск потока
thread = threading.Thread(target=worker, args=("Поток-1", 2))
thread.start()
thread.join() # Ожидание завершения потока
GIL (Global Interpreter Lock)
Одна из ключевых особенностей Python — Global Interpreter Lock (GIL). Это механизм, который позволяет только одному потоку выполнять байт-код Python одновременно, даже на многоядерных системах. Поэтому мультитрединг в Python НЕ дает истинный параллелизм для CPU-bound задач.
import threading
import time
def cpu_bound_task():
total = 0
for i in range(100_000_000):
total += i
return total
# Последовательное выполнение
start = time.time()
cpu_bound_task()
cpu_bound_task()
print(f"Последовательно: {time.time() - start:.2f}s")
# С мультитредингом (медленнее из-за GIL)
start = time.time()
t1 = threading.Thread(target=cpu_bound_task)
t2 = threading.Thread(target=cpu_bound_task)
t1.start()
t2.start()
t1.join()
t2.join()
print(f"Мультитредингом: {time.time() - start:.2f}s") # Медленнее!
Когда использовать мультитрединг
Мультитрединг эффективен для I/O-bound задач, когда программа ожидает ввода-вывода (сетевые запросы, чтение файлов, работа с БД):
import threading
import requests
def fetch_data(url):
response = requests.get(url)
print(f"Получены данные с {url}")
return response.status_code
urls = ["https://api.example.com/1", "https://api.example.com/2"]
threads = []
for url in urls:
t = threading.Thread(target=fetch_data, args=(url,))
threads.append(t)
t.start()
for t in threads:
t.join()
В этом примере потоки ждут сетевого ответа независимо друг от друга, что позволяет значительно ускорить выполнение.
Синхронизация между потоками
При одновременной работе потоков часто возникают проблемы с доступом к общим данным. Для синхронизации используются:
Lock (Блокировка):
import threading
lock = threading.Lock()
counter = 0
def increment():
global counter
with lock:
counter += 1
threads = [threading.Thread(target=increment) for _ in range(1000)]
for t in threads:
t.start()
for t in threads:
t.join()
print(f"Счётчик: {counter}") # Всегда 1000
Condition (Условная переменная) — для синхронизации событий между потоками:
import threading
condition = threading.Condition()
data = None
def producer():
global data
with condition:
data = "Новые данные"
condition.notify_all() # Уведомляем ожидающие потоки
def consumer():
with condition:
condition.wait() # Ждём уведомления
print(f"Получены: {data}")
Queue — потокобезопасная очередь для передачи данных между потоками:
from queue import Queue
import threading
q = Queue()
def producer():
for i in range(5):
q.put(i)
def consumer():
while True:
item = q.get()
if item is None:
break
print(f"Обработан: {item}")
q.task_done()
t1 = threading.Thread(target=producer)
t2 = threading.Thread(target=consumer)
t1.start()
t2.start()
Альтернативы мультитредингу
- multiprocessing — для CPU-bound задач, обходит GIL
- asyncio — для I/O-bound задач, однопоточная асинхронность
- concurrent.futures — удобные абстракции ThreadPoolExecutor и ProcessPoolExecutor
Выбор между ними зависит от типа задачи и требований к производительности.