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

Как передать данные между потоками?

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

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

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

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

Передача данных между потоками в Python

Основные способы

В Python существует несколько механизмов для безопасного обмена данными между потоками. Рассмотрим каждый из них.

1. Queue (очередь) — самый безопасный способ

Модуль queue.Queue предоставляет потокобезопасную очередь — лучший выбор для синхронизации потоков:

import queue
import threading
import time

# Создаем потокобезопасную очередь
q = queue.Queue(maxsize=10)

def producer():
    for i in range(5):
        time.sleep(0.5)
        data = f"Данные {i}"
        q.put(data)  # Отправляем данные
        print(f"Отправлено: {data}")

def consumer():
    while True:
        try:
            data = q.get(timeout=2)  # Получаем с таймаутом
            print(f"Получено: {data}")
            q.task_done()  # Сигнализируем об окончании обработки
        except queue.Empty:
            print("Очередь пуста")
            break

t1 = threading.Thread(target=producer)
t2 = threading.Thread(target=consumer)

t1.start()
t2.start()

q.join()  # Ждем, пока все задачи обработаны
t1.join()
t2.join()

2. Lock и RLock — для защиты общих ресурсов

Используем блокировки для синхронизации доступа к общим переменным:

import threading

class SharedData:
    def __init__(self):
        self.value = 0
        self.lock = threading.Lock()
    
    def increment(self):
        with self.lock:  # Экстьм доступ к value
            temp = self.value
            temp += 1
            self.value = temp

shared = SharedData()

def worker():
    for _ in range(1000):
        shared.increment()

threads = [threading.Thread(target=worker) for _ in range(5)]
for t in threads:
    t.start()
for t in threads:
    t.join()

print(f"Финальное значение: {shared.value}")  # 5000

3. Condition — сигнализация между потоками

Используем условные переменные для уведомления потоков о событиях:

import threading
import time

condition = threading.Condition()
data = None

def producer():
    global data
    for i in range(3):
        time.sleep(1)
        with condition:
            data = f"Данные {i}"
            print(f"Произведено: {data}")
            condition.notify_all()  # Уведомляем потребителей

def consumer(name):
    global data
    while True:
        with condition:
            condition.wait()  # Ожидаем уведомления
            if data:
                print(f"Потребитель {name} получил: {data}")

t1 = threading.Thread(target=producer)
t2 = threading.Thread(target=consumer, args=("A",))
t3 = threading.Thread(target=consumer, args=("B",))

t1.start()
t2.start()
t3.start()

4. Event — простая сигнализация

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

import threading
import time

event = threading.Event()

def waiter():
    print("Поток ждет события...")
    event.wait()  # Блокируется до set()
    print("Событие произошло!")

def setter():
    time.sleep(2)
    event.set()  # Устанавливаем флаг

t1 = threading.Thread(target=waiter)
t2 = threading.Thread(target=setter)

t1.start()
t2.start()

t1.join()
t2.join()

5. Semaphore — ограничение доступа

Семафор контролирует количество потоков, одновременно обращающихся к ресурсу:

import threading
import time

# Максимум 2 потока могут одновременно работать
sem = threading.Semaphore(2)

def worker(name):
    with sem:
        print(f"Поток {name} работает")
        time.sleep(1)
        print(f"Поток {name} закончил")

threads = [threading.Thread(target=worker, args=(i,)) for i in range(5)]
for t in threads:
    t.start()
for t in threads:
    t.join()

Рекомендации по выбору

  • Queue — предпочитаемый способ для обмена данными между потоками
  • Lock — для защиты критических секций кода
  • Event — для простых сигналов готовности
  • Condition — для сложной синхронизации с уведомлениями
  • Semaphore — для контроля количества одновременных обращений

Основной принцип: избегайте гонки данных, используя синхронизацию. Queue часто безопаснее, чем прямой доступ к общим переменным с Lock.

Как передать данные между потоками? | PrepBro