Зачем нужен модуль threading?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Модуль 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 остаётся мощным инструментом для правильных задач.