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

Можно ли с помощью мультитрендинга создать несколько потоков?

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

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

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

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

Многопоточность в Python: создание потоков

Отвечу сразу: да, с помощью модуля threading можно создавать несколько потоков, но есть важная оговорка — GIL (Global Interpreter Lock) ограничивает их параллелизм.

Основной способ: threading.Thread

import threading
import time

def worker(name: str, delay: float):
    """Функция, которую будет выполнять поток"""
    print(f"{name} начал работу")
    time.sleep(delay)
    print(f"{name} закончил работу")

# Способ 1: Создание потока через Thread класс
thread1 = threading.Thread(target=worker, args=("Поток-1", 2))
thread2 = threading.Thread(target=worker, args=("Поток-2", 1))

# Запуск потоков
thread1.start()
thread2.start()

# Ожидание завершения
thread1.join()
thread2.join()

print("Все потоки завершены")

Создание потоков через подклассирование

class MyThread(threading.Thread):
    def __init__(self, name: str, value: int):
        super().__init__()
        self.name = name
        self.value = value
    
    def run(self):
        """Переопределяем run для выполнения потока"""
        print(f"{self.name}: вычисляю сумму")
        total = sum(range(1, self.value + 1))
        print(f"{self.name}: результат = {total}")

t1 = MyThread("T1", 1000)
t2 = MyThread("T2", 5000)
t1.start()
t2.start()
t1.join()
t2.join()

ThreadPoolExecutor: управление пулом потоков

from concurrent.futures import ThreadPoolExecutor
import requests

def fetch_url(url: str) -> tuple[str, int]:
    response = requests.get(url, timeout=5)
    return url, response.status_code

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

with ThreadPoolExecutor(max_workers=3) as executor:
    results = executor.map(fetch_url, urls)
    for url, status in results:
        print(f"{url}: {status}")

GIL: главное ограничение потоков в Python

GIL (Global Interpreter Lock) — мьютекс, который позволяет только одному потоку выполнять Python код одновременно.

import threading
import time

def cpu_bound_work():
    """CPU-интенсивная работа"""
    total = 0
    for i in range(100_000_000):
        total += i
    return total

# Однопоточное выполнение
start = time.time()
cpu_bound_work()
cpu_bound_work()
print(f"Однопоточно: {time.time() - start:.2f}s")  # около 10 сек

# Двухпоточное выполнение (медленнее из-за GIL)
start = time.time()
t1 = threading.Thread(target=cpu_bound_work)
t2 = threading.Thread(target=cpu_bound_work)
t1.start()
t2.start()
t1.join()
t2.join()
print(f"Двухпоточно: {time.time() - start:.2f}s")  # медленнее!

Когда threading эффективен

I/O-bound операции (потоки НЕ блокируются на GIL):

import threading
import time
from concurrent.futures import ThreadPoolExecutor

def io_operation(name: str):
    """I/O операция (сеть, диск, БД)"""
    print(f"{name}: начало I/O")
    time.sleep(2)  # Имитируем сетевой запрос
    print(f"{name}: конец I/O")

# Однопоточно: 6 сек (2 сек на 3 операции)
start = time.time()
io_operation("op1")
io_operation("op2")
io_operation("op3")
print(f"Результат: {time.time() - start:.2f}s")  # 6 сек

# С потоками: примерно 2 сек (параллельно!)
start = time.time()
with ThreadPoolExecutor(max_workers=3) as executor:
    executor.map(io_operation, ["op1", "op2", "op3"])
print(f"Результат: {time.time() - start:.2f}s")  # около 2 сек

Синхронизация потоков

import threading

class Counter:
    def __init__(self):
        self.value = 0
        self.lock = threading.Lock()  # Мьютекс
    
    def increment(self):
        # Без lock — race condition!
        with self.lock:  # Атомная операция
            self.value += 1

counter = Counter()
threads = [
    threading.Thread(target=counter.increment)
    for _ in range(100)
]

for t in threads:
    t.start()
for t in threads:
    t.join()

print(counter.value)  # Всегда 100 (без lock был бы меньше)

Многопоточность vs асинхронность

# Threading: система сама переключает между потоками (OS threads, тяжело)
# Asyncio: кооперативное переключение в одном потоке (легче, быстрее)

import asyncio

async def async_io_operation(name: str):
    print(f"{name}: начало")
    await asyncio.sleep(2)  # Неблокирующая операция
    print(f"{name}: конец")

# примерно 2 сек вместо 6
import time
start = time.time()
asyncio.run(asyncio.gather(
    async_io_operation("op1"),
    async_io_operation("op2"),
    async_io_operation("op3"),
))
print(f"Результат: {time.time() - start:.2f}s")

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

  1. Для I/O: threading или asyncio ✅
  2. Для CPU-bound: multiprocessing необходим ✅
  3. Правило: если код содержит I/O или ожидание → threading поможет
  4. Правило: если код = цикл с вычислениями → multiprocessing нужен

Вывод

Да, можно создавать потоки через threading. Это работает отлично для I/O-операций (API запросы, файлы, БД), но для CPU-интенсивной работы нужно использовать multiprocessing, потому что GIL блокирует истинный параллелизм.