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

Какие типы задач решаются с помощью модуля multithreading в Python?

2.0 Middle🔥 161 комментариев
#Python Core#Асинхронность и многопоточность

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

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

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

Модуль multithreading в Python: типы задач

Модуль multithreading в Python предназначен для параллельного выполнения кода внутри одного процесса. Несмотря на GIL (Global Interpreter Lock), он успешно решает многие классы задач. Рассмотрим основные типы:

1. I/O-bound задачи (основной случай использования)

Это задачи, которые тратят большую часть времени на ожидание внешних ресурсов, а не на вычисления:

import threading
import requests
import time

def fetch_url(url):
    response = requests.get(url)  # Блокирующий I/O
    print(f"Загружено: {len(response.content)} байт")

# Без многопоточности: ~3 секунды
start = time.time()
fetch_url("https://api.example.com/data1")
fetch_url("https://api.example.com/data2")
fetch_url("https://api.example.com/data3")
print(f"Время: {time.time() - start}s")  # ~3s

# С многопоточностью: ~1 секунда
start = time.time()
threads = [
    threading.Thread(target=fetch_url, args=(url,))
    for url in [
        "https://api.example.com/data1",
        "https://api.example.com/data2",
        "https://api.example.com/data3"
    ]
]
for t in threads:
    t.start()
for t in threads:
    t.join()
print(f"Время: {time.time() - start}s")  # ~1s

2. Работа с сетью

Загрузка данных с веб-сервисов, API запросы, парсинг веб-страниц:

from threading import Thread, Lock
from queue import Queue
import requests

class URLFetcher:
    def __init__(self, num_workers=4):
        self.queue = Queue()
        self.results = {}
        self.lock = Lock()
        self.workers = [
            Thread(target=self._worker, daemon=True)
            for _ in range(num_workers)
        ]
        for w in self.workers:
            w.start()
    
    def _worker(self):
        while True:
            url = self.queue.get()
            try:
                resp = requests.get(url)
                with self.lock:
                    self.results[url] = resp.status_code
            except Exception as e:
                with self.lock:
                    self.results[url] = str(e)
            finally:
                self.queue.task_done()
    
    def fetch_many(self, urls):
        for url in urls:
            self.queue.put(url)
        self.queue.join()
        return self.results

3. Работа с файловой системой

Чтение/запись множества файлов параллельно:

import threading
import os
from pathlib import Path

def process_file(filepath, output_dir):
    with open(filepath, r) as f:
        content = f.read()
    # Обработка
    result = content.upper()
    output_path = Path(output_dir) / Path(filepath).name
    with open(output_path, w) as f:
        f.write(result)

files = [f"file{i}.txt" for i in range(100)]
threads = [
    threading.Thread(target=process_file, args=(f, "output/"))
    for f in files
]

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

4. Долгоживущие фоновые задачи

Демон-потоки для мониторинга, логирования, очистки ресурсов:

import threading
import time
from queue import Queue

class LogWriter:
    def __init__(self):
        self.log_queue = Queue()
        self.daemon_thread = threading.Thread(
            target=self._write_logs,
            daemon=True  # Завершится с основной программой
        )
        self.daemon_thread.start()
    
    def _write_logs(self):
        """Непрерывно пишет логи из очереди в файл"""
        with open(app.log, a) as f:
            while True:
                message = self.log_queue.get()
                if message is None:  # Сигнал завершения
                    break
                f.write(message + 
)
                f.flush()
    
    def log(self, message):
        self.log_queue.put(message)
    
    def stop(self):
        self.log_queue.put(None)
        self.daemon_thread.join()

5. Асинхронные операции в веб-приложениях

Обработка нескольких клиентских подключений одновременно:

import threading
import socket

def handle_client(client_socket, address):
    """Обработчик для каждого клиента в отдельном потоке"""
    try:
        request = client_socket.recv(1024).decode()
        response = b"HTTP/1.1 200 OK\r\n\r\nHello!"
        client_socket.send(response)
    finally:
        client_socket.close()

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("localhost", 8080))
server.listen(5)

while True:
    client_socket, address = server.accept()
    # Каждый клиент обрабатывается в своём потоке
    thread = threading.Thread(
        target=handle_client,
        args=(client_socket, address)
    )
    thread.start()

6. Параллельные UI операции

Выполнение тяжёлых операций без блокировки графического интерфейса:

import tkinter as tk
import threading
import time

class App:
    def __init__(self, root):
        self.root = root
        self.button = tk.Button(root, text="Загрузить", command=self.on_click)
        self.button.pack()
        self.label = tk.Label(root, text="")
        self.label.pack()
    
    def on_click(self):
        # Тяжёлая операция в отдельном потоке
        thread = threading.Thread(target=self._heavy_task)
        thread.start()
    
    def _heavy_task(self):
        time.sleep(5)  # Долгая операция
        self.root.after(0, lambda: self.label.config(text="Готово!"))

7. Кэширование и предварительная загрузка данных

import threading
from threading import RLock
import time

class CachePreloader:
    def __init__(self):
        self.cache = {}
        self.lock = RLock()
        self.preloader_thread = threading.Thread(
            target=self._preload,
            daemon=True
        )
        self.preloader_thread.start()
    
    def _preload(self):
        """Фоновая предварительная загрузка популярных данных"""
        while True:
            time.sleep(60)
            with self.lock:
                # Обновляем часто используемые данные
                self.cache[popular] = self._fetch_popular_data()
    
    def _fetch_popular_data(self):
        return {"items": [1, 2, 3]}
    
    def get(self, key):
        with self.lock:
            return self.cache.get(key)

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

# ❌ CPU-bound задачи (вычисления)
# GIL предотвращает параллелизм
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

# Используй multiprocessing вместо этого
from multiprocessing import Pool

with Pool(4) as p:
    results = p.map(fibonacci, range(30))

Итоговая таблица применения

Тип задачиmultithreadingmultiprocessingasyncio
I/O-bound✅ Хорошо❌ Перебор✅ Лучше
CPU-bound❌ GIL✅ Хорошо❌ Не поможет
Сеть✅ Хорошо❌ Перебор✅ Оптимально
Простота✅ Простой API⚠️ Сложнее⚠️ Async/await

Вывод: multithreading — это инструмент для I/O-bound задач, где потоки могут ждать блокирующих операций параллельно, пока другие потоки работают. Для чистых вычислений нужен multiprocessing, для асинхронного кода — asyncio.

Какие типы задач решаются с помощью модуля multithreading в Python? | PrepBro