← Назад к вопросам

Зачем нужны потоки в 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 проблемаПлюсыМинусы
ThreadingI/O операцииНе проблемаПросто, синхронный стильГонки, deadlock
AsyncioI/O операцииНе проблемаЭффективнее, меньше памятиСложнее, нужен async код
MultiprocessingCPU операцииНет 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))

Лучшие практики

  1. Для I/O: используй threading или asyncio
  2. Для CPU: используй multiprocessing
  3. Для веб: используй asyncio (FastAPI, aiohttp)
  4. Для фронта: используй потоки осторожно
  5. Всегда используй lock для доступа к общим данным
  6. Предпочитай asyncio потокам для современного кода