Какие типы задач решаются с помощью модуля multithreading в Python?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Модуль 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))
Итоговая таблица применения
| Тип задачи | multithreading | multiprocessing | asyncio |
|---|---|---|---|
| I/O-bound | ✅ Хорошо | ❌ Перебор | ✅ Лучше |
| CPU-bound | ❌ GIL | ✅ Хорошо | ❌ Не поможет |
| Сеть | ✅ Хорошо | ❌ Перебор | ✅ Оптимально |
| Простота | ✅ Простой API | ⚠️ Сложнее | ⚠️ Async/await |
Вывод: multithreading — это инструмент для I/O-bound задач, где потоки могут ждать блокирующих операций параллельно, пока другие потоки работают. Для чистых вычислений нужен multiprocessing, для асинхронного кода — asyncio.