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

Зачем нужен модуль threading?

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

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

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

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

Модуль threading в Python

Threading — это модуль для работы с потоками (threads), позволяет выполнять несколько функций одновременно в одном процессе. Это критично для приложений, требующих параллелизма.

Что такое потоки (threads)

Поток — это лёгкий процесс, выполняемый в контексте основного процесса. На одной машине может работать множество потоков одновременно.

Процесс vs Поток:

  • Процесс: отдельное приложение, собственная память, тяжёлый
  • Поток: часть процесса, общая память, лёгкий

Зачем нужен threading

1. Асинхронные операции Если основной код ждёт результата (I/O операция), можно запустить в отдельном потоке.

import threading
import time
import requests

def fetch_data(url):
    # Эта операция блокирует основной поток
    response = requests.get(url)
    print(f\"Got response: {response.status_code}\")

# Без threading — ждём 5 секунд
start = time.time()
fetch_data('https://jsonplaceholder.typicode.com/posts/1')
fetch_data('https://jsonplaceholder.typicode.com/posts/2')
print(f\"Time: {time.time() - start:.2f}s\")  # ~10 сек

# С threading — почти одновременно
start = time.time()
thread1 = threading.Thread(target=fetch_data, args=('https://jsonplaceholder.typicode.com/posts/1',))
thread2 = threading.Thread(target=fetch_data, args=('https://jsonplaceholder.typicode.com/posts/2',))

thread1.start()
thread2.start()

thread1.join()  # Ждём завершения
thread2.join()

print(f\"Time: {time.time() - start:.2f}s\")  # ~5 сек

2. Фоновые задачи Что-то работает в фоне, не блокируя основной код.

class BackgroundWorker:
    def __init__(self):
        self.running = True
    
    def work(self):
        while self.running:
            print(\"Doing background work...\")
            time.sleep(1)
    
    def stop(self):
        self.running = False

# Запуск в фоновом потоке
worker = BackgroundWorker()
thread = threading.Thread(target=worker.work, daemon=True)
thread.start()

# Основной код продолжает работать
for i in range(3):
    print(f\"Main thread: iteration {i}\")
    time.sleep(0.5)

worker.stop()

3. UI приложения В GUI приложениях основной поток отвечает за интерфейс. Длительные операции запускают в отдельных потоках.

import tkinter as tk
import threading
import time

class App:
    def __init__(self, root):
        self.root = root
        self.button = tk.Button(root, text=\"Download\", command=self.on_download)
        self.button.pack()
        self.status = tk.Label(root, text=\"Ready\")
        self.status.pack()
    
    def on_download(self):
        # Запускаем в отдельном потоке, чтобы UI не заморозился
        thread = threading.Thread(target=self.download)
        thread.start()
    
    def download(self):
        self.status.config(text=\"Downloading...\")
        time.sleep(5)  # Имитация долгой операции
        self.status.config(text=\"Done\")

root = tk.Tk()
app = App(root)
root.mainloop()

GIL (Global Interpreter Lock)

Важно! В CPython есть GIL — блокировка, которая позволяет только одному потоку выполнять Python код одновременно.

Это значит:

  • Threading не даёт истинный параллелизм для CPU-bound задач
  • Threading идеален для I/O-bound задач (сетевые запросы, файлы, БД)
import threading
import time

def cpu_intensive(n):
    total = 0
    for i in range(n):
        total += i
    return total

# Без threading — 2 сек
start = time.time()
cpu_intensive(100_000_000)
cpu_intensive(100_000_000)
print(f\"Sequential: {time.time() - start:.2f}s\")  # ~2 сек

# С threading — всё ещё ~2 сек (GIL!)
start = time.time()
thread1 = threading.Thread(target=cpu_intensive, args=(100_000_000,))
thread2 = threading.Thread(target=cpu_intensive, args=(100_000_000,))
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(f\"Threading: {time.time() - start:.2f}s\")  # ~2 сек, не быстрее!

Решение для CPU-bound задач: использовать multiprocessing (отдельные процессы).

Основные функции threading

Thread класс:

import threading

class MyThread(threading.Thread):
    def __init__(self, name):
        super().__init__()
        self.name = name
    
    def run(self):
        print(f\"Starting {self.name}\")
        time.sleep(2)
        print(f\"Ending {self.name}\")

# Создание и запуск
thread = MyThread(\"Worker\")
thread.start()  # Запускает run()
thread.join()   # Ждёт завершения
print(f\"Is alive: {thread.is_alive()}\")  # False

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(10)]
for t in threads:
    t.start()
for t in threads:
    t.join()

print(f\"Counter: {counter}\")  # 10 (правильно)

Event для синхронизации потоков:

stop_event = threading.Event()

def worker():
    while not stop_event.is_set():
        print(\"Working...\")
        time.sleep(1)

thread = threading.Thread(target=worker)
thread.start()

time.sleep(3)
stop_event.set()  # Сигнализирует потоку остановиться
thread.join()
print(\"Stopped\")

Queue для обмена данными между потоками:

from queue import Queue
import threading

def producer(queue):
    for i in range(5):
        queue.put(f\"Item {i}\")
        time.sleep(0.5)

def consumer(queue):
    while True:
        item = queue.get()
        print(f\"Processing {item}\")
        queue.task_done()

q = Queue()
thread1 = threading.Thread(target=producer, args=(q,))
thread2 = threading.Thread(target=consumer, args=(q,))

thread1.start()
thread2.start()

thread1.join()
q.join()  # Ждёт, пока все элементы обработаны

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

Подходит:

  • I/O операции (сетевые запросы, файлы, БД)
  • Фоновые задачи
  • UI приложения
  • Обработка множества соединений (но лучше asyncio)

НЕ подходит:

  • CPU-intensive операции (используй multiprocessing)
  • Простые задачи (overhead может быть больше пользы)
  • Когда нужна сложная синхронизация

Альтернативы threading

asyncio — асинхронное программирование (современнее, для I/O):

import asyncio

async def fetch(url):
    # Асинхронный запрос
    return f\"Response from {url}\"

async def main():
    results = await asyncio.gather(
        fetch('url1'),
        fetch('url2'),
        fetch('url3')
    )
    print(results)

asyncio.run(main())

multiprocessing — отдельные процессы для CPU-bound задач:

from multiprocessing import Process, Queue

def cpu_task(n, q):
    q.put(sum(range(n)))

if __name__ == '__main__':
    q = Queue()
    p = Process(target=cpu_task, args=(1_000_000, q))
    p.start()
    result = q.get()
    p.join()

Выводы

  • Threading идеален для I/O-bound задач
  • GIL не позволяет истинный параллелизм для CPU-bound
  • asyncio современнее для I/O
  • multiprocessing для CPU-bound
  • Всегда синхронизируй доступ к общим данным (Lock, Queue, Event)

Threading остаётся мощным инструментом для правильных задач.