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

Что такое многопоточность (multithreading)?

1.3 Junior🔥 211 комментариев
#Асинхронность и многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Многопоточность (Multithreading) в Python

Определение

Многопоточность — это способ организации выполнения кода, при котором несколько потоков (threads) выполняются в одном процессе, разделяя одно адресное пространство памяти. Это позволяет выполнять несколько операций параллельно.

Основные концепции

Что такое поток?

Поток — это легковесный объект внутри процесса, который может выполнять код независимо от других потоков.

import threading
import time

def worker(name):
    for i in range(3):
        print(f"{name}: работаю {i+1}")
        time.sleep(1)

# Создание и запуск потока
thread = threading.Thread(target=worker, args=("Поток-1",))
thread.start()  # Запуск потока
thread.join()   # Ожидание завершения

Проблема GIL (Global Interpreter Lock)

Это критически важный концепт для понимания многопоточности в Python!

Что это?

GIL — это мьютекс, который позволяет только одному потоку одновременно выполнять байт-код Python. Это означает, что даже на многоядерной системе только один поток может выполнять код Python в данный момент.

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")  # 7 секунд

# С многопоточностью (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")  # ВСЁ ЕЩЕ 7 секунд!

Почему это плохо?

ГIL делает невозможным истинный параллелизм для CPU-bound операций, так что многопоточность в Python НЕ ускоряет вычисления.

Когда многопоточность полезна

Многопоточность отлично работает для I/O-bound операций (сетевые запросы, чтение файлов, запросы БД):

import threading
import requests
import time

def fetch_url(url):
    response = requests.get(url)
    print(f"Загрузил {url}: {len(response.content)} байт")
    return response

urls = [
    "https://example.com",
    "https://google.com",
    "https://github.com",
]

# Без многопоточности
start = time.time()
for url in urls:
    fetch_url(url)
print(f"Последовательно: {time.time() - start:.2f}s")  # 9 секунд

# С многопоточностью (НАМНОГО быстрее!)
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")  # 3 секунды

Время ожидания I/O операций не блокирует другие потоки, поэтому они могут продолжать работу.

Thread Pool (ThreadPoolExecutor)

Для управления большим количеством потоков лучше использовать пул потоков:

from concurrent.futures import ThreadPoolExecutor
import requests

urls = ["https://example.com"] * 100

# Пул из 5 потоков
with ThreadPoolExecutor(max_workers=5) as executor:
    results = executor.map(requests.get, urls)
    
print(f"Загрузил {len(list(results))} страниц")

Преимущества:

  • Автоматическое управление жизненным циклом потоков
  • Ограничение количества активных потоков
  • Простой API для обработки результатов

Синхронизация между потоками

Lock (Мьютекс)

import threading

counter = 0
lock = threading.Lock()

def increment():
    global counter
    with lock:  # Только один поток может выполнять код внутри
        temp = counter
        temp += 1
        counter = temp

threads = [threading.Thread(target=increment) for _ in range(100)]
for t in threads:
    t.start()
for t in threads:
    t.join()

print(counter)  # 100 (без lock была бы ошибка)

Event (Сигнал)

import threading
import time

event = threading.Event()

def waiter():
    print("Жду сигнал...")
    event.wait()  # Блокирует, пока Event не будет set
    print("Сигнал получен!")

def signaler():
    time.sleep(2)
    print("Отправляю сигнал")
    event.set()  # Пробуждает все потоки, ждущие event

t1 = threading.Thread(target=waiter)
t2 = threading.Thread(target=signaler)
t1.start()
t2.start()
t1.join()
t2.join()

Semaphore (Семафор)

import threading
import time

sem = threading.Semaphore(2)  # Максимум 2 потока одновременно

def limited_resource():
    with sem:
        print(f"{threading.current_thread().name} использует ресурс")
        time.sleep(1)

threads = [threading.Thread(target=limited_resource) for _ in range(5)]
for t in threads:
    t.start()
for t in threads:
    t.join()

Альтернативы многопоточности

Asyncio (асинхронность)

Для I/O-bound операций asyncio часто лучше, чем многопоточность:

import asyncio
import aiohttp

async def fetch(session, url):
    async with session.get(url) as response:
        return await response.text()

async def main():
    urls = ["https://example.com"] * 10
    async with aiohttp.ClientSession() as session:
        tasks = [fetch(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
    print(f"Загрузил {len(results)} страниц")

asyncio.run(main())

Multiprocessing

Для CPU-bound операций используй процессы, а не потоки:

from multiprocessing import Process
import time

def cpu_bound():
    total = 0
    for i in range(100_000_000):
        total += i
    return total

if __name__ == "__main__":
    start = time.time()
    p1 = Process(target=cpu_bound)
    p2 = Process(target=cpu_bound)
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print(f"Два процесса: {time.time() - start:.2f}s")  # 3.5 секунды!

Когда использовать

  • Многопоточность: I/O-bound операции (HTTP, БД, файлы)
  • Asyncio: I/O-bound с большим количеством операций
  • Multiprocessing: CPU-bound операции

Проблемы многопоточности

  • Race conditions (состояние гонки)
  • Deadlocks (взаимная блокировка)
  • Сложность отладки
  • GIL ограничивает параллелизм

Вывод

Многопоточность в Python — мощный инструмент для асинхронных операций ввода-вывода, но имеет ограничения из-за GIL для CPU-bound задач. Для больших систем рассмотри asyncio или multiprocessing в зависимости от типа нагрузки.

Что такое многопоточность (multithreading)? | PrepBro