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

Где встречается гонка данных в Python?

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

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

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

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

Где встречается гонка данных в Python?

Гонка данных (race condition) — это состояние, когда результат программы зависит от того, в каком порядке несколько потоков выполняют свои операции. В Python это явление встречается часто, несмотря на наличие GIL (Global Interpreter Lock).

GIL и многопоточность

Многие разработчики думают, что GIL полностью защищает от race conditions. Это ошибка. GIL защищает внутренние структуры интерпретатора Python от повреждения, но НЕ защищает данные приложения. GIL может быть отпущен даже во время выполнения Python кода, особенно при операциях с I/O.

Основные сценарии race conditions

1. Обновление общих переменных из разных потоков

import threading

counter = 0

def increment():
    global counter
    for _ in range(100000):
        counter += 1

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

print(counter)  # Ожидаем 500000, но получим меньше

Операция counter += 1 не атомарна. Она состоит из трёх шагов:

  1. Прочитать значение counter
  2. Увеличить его
  3. Записать обратно

Между этими шагами GIL может быть отпущен, и другой поток может прочитать старое значение.

2. Работа со списками и словарями

shared_list = []

def append_items():
    for i in range(1000):
        shared_list.append(i)

def read_items():
    while True:
        if shared_list:
            _ = len(shared_list)  # Race condition!

thread1 = threading.Thread(target=append_items)
thread2 = threading.Thread(target=read_items)
thread1.start()
thread2.start()

Хотя операции со списками в CPython часто атомарны, это не гарантировано для всех типов операций и других реализаций Python.

3. Проверка и установка (check-then-act)

if resource.is_available():
    resource.allocate()  # Race condition: другой поток может захватить ресурс

Между проверкой и захватом ресурса может пройти другой поток.

4. Асинхронный код (asyncio)

async def process():
    global state
    state = "processing"
    await some_io_operation()  # GIL отпущен здесь
    state = "done"  # Другой task мог изменить state

При await другая корутина может выполниться и изменить общее состояние.

Защита от race conditions

Использование Lock

import threading

counter = 0
lock = threading.Lock()

def increment():
    global counter
    for _ in range(100000):
        with lock:
            counter += 1

Использование Queue (потокобезопасная очередь)

from queue import Queue

q = Queue()

def producer():
    for i in range(100):
        q.put(i)

def consumer():
    while True:
        item = q.get()
        # обработка
        q.task_done()

Использование RLock для рекурсивных блокировок

rlock = threading.RLock()

with rlock:
    # Один поток может захватить рекурсивно несколько раз
    with rlock:
        pass

Использование Semaphore/Condition Variable

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

with semaphore:
    # выполнение

В asyncio используй asyncio.Lock

lock = asyncio.Lock()

async def safe_operation():
    async with lock:
        # критическая секция

Почему это сложно в Python

  • GIL дает ложное чувство безопасности — разработчики думают, что заблокированы
  • Непредсказуемость — race condition может проявиться только при определённом количестве потоков или нагрузке
  • Сложность отладки — проблемы редко воспроизводятся стабильно
  • Реализация зависит от версии — поведение может отличаться в CPython, PyPy, Jython

Знание о race conditions критично для написания надёжного многопоточного кода в Python.

Где встречается гонка данных в Python? | PrepBro