← Назад к вопросам
Зачем нужны потоки в Python?
2.0 Middle🔥 241 комментариев
#Python Core#Асинхронность и многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Зачем нужны потоки в Python
Определение
Поток (thread) — это легковесный процесс выполнения, который может выполняться внутри одного процесса. Потоки позволяют выполнять несколько задач одновременно в рамках одного приложения.
Проблема: GIL (Global Interpreter Lock)
Важно понимать, что Python имеет Global Interpreter Lock (GIL), который позволяет только одному потоку выполнять Python код одновременно:
import threading
import time
def cpu_bound_task(n):
"""CPU-intensive задача"""
total = 0
for i in range(n):
total += i
return total
# Синхронное выполнение
start = time.time()
cpu_bound_task(100000000)
cpu_bound_task(100000000)
print(f"Синхронно: {time.time() - start:.2f}s") # ~3.0 сек
# С потоками — НЕ быстрее!
start = time.time()
t1 = threading.Thread(target=cpu_bound_task, args=(100000000,))
t2 = threading.Thread(target=cpu_bound_task, args=(100000000,))
t1.start()
t2.start()
t1.join()
t2.join()
print(f"С потоками: {time.time() - start:.2f}s") # ~3.5 сек (медленнее!)
Когда потоки полезны
1. I/O-bound операции (Блокирующие операции)
Потоки ПОЛЕЗНЫ для блокирующих операций (сеть, файлы, БД), потому что GIL отпускается:
import threading
import requests
import time
def fetch_url(url):
"""Блокирующий HTTP запрос"""
response = requests.get(url)
return response.status_code
urls = [
"https://httpbin.org/delay/2",
"https://httpbin.org/delay/2",
"https://httpbin.org/delay/2",
]
# Синхронно
start = time.time()
for url in urls:
fetch_url(url)
print(f"Синхронно: {time.time() - start:.2f}s") # ~6 сек
# С потоками
start = time.time()
threads = []
for url in urls:
t = threading.Thread(target=fetch_url, args=(url,))
t.start()
threads.append(t)
for t in threads:
t.join()
print(f"С потоками: {time.time() - start:.2f}s") # ~2 сек!
2. UI приложения
Потоки нужны, чтобы не заморозить UI при длительных операциях:
import threading
import tkinter as tk
from tkinter import ttk
def long_operation():
"""Длительная операция"""
import time
time.sleep(5) # Имитация долгой работы
print("Готово!")
def on_button_click():
# НЕПРАВИЛЬНО — замораживает UI
# long_operation()
# ПРАВИЛЬНО — выполняет в отдельном потоке
thread = threading.Thread(target=long_operation)
thread.start()
root = tk.Tk()
button = tk.Button(root, text="Запустить", command=on_button_click)
button.pack()
root.mainloop()
Использование threading
Простое использование
import threading
def worker(name, delay):
import time
print(f"Работник {name} начал")
time.sleep(delay)
print(f"Работник {name} закончил")
# Создание потока
thread = threading.Thread(
target=worker,
args=("A", 2),
name="WorkerA"
)
# Запуск
thread.start()
# Ожидание завершения
thread.join()
print("Все готово")
Потокобезопасность с Lock
import threading
data = 0
lock = threading.Lock()
def increment():
global data
for _ in range(100000):
# БЕЗ lock — race condition!
# data += 1
# С lock — безопасно
with lock:
data += 1
threads = [threading.Thread(target=increment) for _ in range(5)]
for t in threads:
t.start()
for t in threads:
t.join()
print(data) # 500000 (правильно)
Thread-safe очередь
import threading
import queue
import time
q = queue.Queue()
def producer():
for i in range(5):
print(f"Производитель: {i}")
q.put(i)
time.sleep(0.5)
def consumer():
while True:
item = q.get()
print(f"Потребитель: {item}")
q.task_done()
t1 = threading.Thread(target=producer)
t2 = threading.Thread(target=consumer, daemon=True)
t1.start()
t2.start()
t1.join()
q.join()
Потоки vs Асинхронность vs Процессы
| Подход | Для чего | GIL проблема | Плюсы | Минусы |
|---|---|---|---|---|
| Threading | I/O операции | Не проблема | Просто, синхронный стиль | Гонки, deadlock |
| Asyncio | I/O операции | Не проблема | Эффективнее, меньше памяти | Сложнее, нужен async код |
| Multiprocessing | CPU операции | Нет GIL | Истинный параллелизм | Дорого, медленно |
Когда НЕ использовать потоки
# ПЛОХО — CPU-bound с потоками
import threading
def cpu_task():
total = sum(i**2 for i in range(100000000))
return total
# Медленнее, чем синхронно!
threads = [threading.Thread(target=cpu_task) for _ in range(4)]
# ХОРОШО — используем multiprocessing для CPU-bound
from multiprocessing import Pool
with Pool(4) as p:
results = p.map(cpu_task, range(4))
Лучшие практики
- Для I/O: используй threading или asyncio
- Для CPU: используй multiprocessing
- Для веб: используй asyncio (FastAPI, aiohttp)
- Для фронта: используй потоки осторожно
- Всегда используй lock для доступа к общим данным
- Предпочитай asyncio потокам для современного кода