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

Как называется код, который нужно оборачивать в Mutex?

2.0 Middle🔥 111 комментариев
#Python Core

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

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

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

Критическая секция (Critical Section) — что это?

Код, который нужно оборачивать в Mutex, называется критической секцией (Critical Section). Это фундаментальное понятие в многопоточном программировании.

Определение

Критическая секция — это участок кода, в котором один поток получает доступ к общему ресурсу (переменная, файл, база данных). Если несколько потоков будут выполнять этот код одновременно, возникнет race condition (состояние гонки).

Пример проблемы (без Mutex)

import threading

counter = 0

def increment():
    global counter
    # Это — критическая секция!
    for _ in range(1000000):
        counter += 1

def decrement():
    global counter
    # Это — критическая секция!
    for _ in range(1000000):
        counter -= 1

t1 = threading.Thread(target=increment)
t2 = threading.Thread(target=decrement)

t1.start()
t2.start()
t1.join()
t2.join()

print(f"Результат: {counter}")  # Может быть 0, -100, 50 или другое значение!

Проблема: операция counter += 1 в Python это три действия:

  1. Прочитать значение из памяти
  2. Добавить 1
  3. Записать обратно

Поток может быть прерван между шагами, и другой поток прочитает старое значение. Результат непредсказуем.

Решение: оборачивание Mutex

import threading

counter = 0
mutex = threading.Lock()

def increment():
    global counter
    for _ in range(1000000):
        with mutex:  # Критическая секция
            counter += 1

def decrement():
    global counter
    for _ in range(1000000):
        with mutex:  # Критическая секция
            counter -= 1

t1 = threading.Thread(target=increment)
t2 = threading.Thread(target=decrement)

t1.start()
t2.start()
t1.join()
t2.join()

print(f"Результат: {counter}")  # Всегда 0!

Как это работает:

  • Только один поток может находиться внутри with mutex: одновременно
  • Другие потоки ждут, пока первый завершит работу
  • Гарантируется консистентность данных

Типы критических секций

1. Read-Modify-Write (RMW)

Это самый распространённый тип:

# Критическая секция: прочитать-изменить-написать
with mutex:
    value = shared_data['balance']  # Read
    value -= 100                    # Modify
    shared_data['balance'] = value  # Write

2. Проверка условия перед действием

with mutex:
    if len(queue) > 0:      # Check
        item = queue.pop()  # Action

Без Mutex другой поток может удалить элемент между Check и Action.

3. Обновление структур данных

with mutex:
    # Вставка в словарь и добавление в список должны быть атомарны
    cache[key] = value
    access_log.append((key, time.time()))

Различные механизмы синхронизации в Python

Lock (Mutex)

import threading

lock = threading.Lock()

with lock:
    # Критическая секция
    pass

RLock (переиспользуемый Mutex)

Если один поток должен несколько раз заблокировать один и тот же Mutex:

import threading

rlock = threading.RLock()

def outer():
    with rlock:
        inner()

def inner():
    with rlock:  # Одна thread может заблокировать RLock несколько раз
        pass

Semaphore

Огранничить доступ нескольким потокам одновременно:

import threading

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

with sem:
    # Критическая секция
    pass

Event и Condition Variable

import threading

condition = threading.Condition()

# Поток 1: ждёт условие
with condition:
    condition.wait()  # Освобождает lock и ждёт
    # Критическая секция

# Поток 2: сигнализирует
with condition:
    # Критическая секция
    condition.notify()  # Пробуждает ждущие потоки

Правила для критических секций

1. Минимизируйте размер критической секции

# Плохо: все операции внутри Mutex
with mutex:
    result = expensive_calculation()  # Это может занять время!
    shared_data = result

# Хорошо: только необходимое внутри
result = expensive_calculation()
with mutex:
    shared_data = result

2. Избегайте вложенных критических секций (deadlock)

# Плохо: может привести к deadlock
with lock1:
    with lock2:
        critical_section()

# Лучше: всегда берите в одном порядке
with lock1, lock2:
    critical_section()

3. Не вызывайте блокирующие операции внутри критической секции

# Плохо: I/O внутри Mutex
with mutex:
    response = requests.get('http://api.example.com')  # Может зависнуть!
    process(response)

# Хорошо: I/O вне Mutex
response = requests.get('http://api.example.com')
with mutex:
    process(response)

Пример: потокобезопасный кэш

import threading
from typing import Any, Optional

class ThreadSafeCache:
    def __init__(self):
        self._cache = {}
        self._lock = threading.Lock()
    
    def get(self, key: str) -> Optional[Any]:
        with self._lock:  # Критическая секция: чтение
            return self._cache.get(key)
    
    def set(self, key: str, value: Any) -> None:
        with self._lock:  # Критическая секция: запись
            self._cache[key] = value
    
    def delete(self, key: str) -> None:
        with self._lock:  # Критическая секция: удаление
            self._cache.pop(key, None)
    
    def clear(self) -> None:
        with self._lock:  # Критическая секция: очистка
            self._cache.clear()

Заключение

  • Критическая секция — это код, работающий с общими ресурсами
  • Mutex (Lock) защищает критическую секцию от одновременного доступа
  • Правило: если несколько потоков могут одновременно читать/писать переменную → это критическая секция
  • Во время разработки спросите: какие переменные разделяют мои потоки? Их должны защищать Mutex!