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

Что такое мьютекс?

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

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

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

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

Мьютекс

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

Основной принцип

Мьютекс работает как бинарный замок:

  • Заблокирован (locked) — ресурс занят одним потоком
  • Разблокирован (unlocked) — ресурс свободен

Когда поток попытается получить заблокированный мьютекс, он будет заблокирован (suspend) и ждать освобождения ресурса.

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

В Python основной инструмент — threading.Lock() или threading.RLock():

import threading

lock = threading.Lock()

balance = 0

def deposit_with_lock(amount):
    global balance
    with lock:
        temp = balance
        temp += amount
        balance = temp

thread1 = threading.Thread(target=deposit_with_lock, args=(100,))
thread2 = threading.Thread(target=deposit_with_lock, args=(200,))
thread1.start()
thread2.start()
thread1.join()
thread2.join()

RLock (Re-entrant Lock)

RLock позволяет одному потоку несколько раз заблокировать один мьютекс:

import threading

lock = threading.RLock()

def recursive_function(n):
    with lock:
        if n > 0:
            print(f"Level {n}")
            recursive_function(n - 1)

recursive_function(3)

Более сложный пример: Thread-Safe Queue

import threading

class ThreadSafeQueue:
    def __init__(self):
        self.items = []
        self.lock = threading.Lock()
    
    def put(self, item):
        with self.lock:
            self.items.append(item)
    
    def get(self):
        with self.lock:
            if self.items:
                return self.items.pop(0)
            return None
    
    def size(self):
        with self.lock:
            return len(self.items)

queue = ThreadSafeQueue()

def producer():
    for i in range(5):
        queue.put(f"item-{i}")
        print(f"Produced item-{i}")

def consumer():
    for _ in range(5):
        item = queue.get()
        if item:
            print(f"Consumed {item}")

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

Проблемы с мьютексами

Deadlock — два потока ждут друг друга:

lock1 = threading.Lock()
lock2 = threading.Lock()

def thread_one():
    with lock1:
        with lock2:
            print("Thread 1")

def thread_two():
    with lock2:
        with lock1:
            print("Thread 2")

Этот код может привести к deadlock'у, если потоки захватят блокировки в противоположном порядке.

Альтернативы с asyncio

import asyncio

lock = asyncio.Lock()

async def critical_section():
    async with lock:
        await asyncio.sleep(1)
        print("Critical section")

async def main():
    await asyncio.gather(
        critical_section(),
        critical_section(),
    )

asyncio.run(main())

Лучшие практики

  • Используй context manager (with lock:) для гарантированного освобождения
  • Минимизируй время внутри критической секции
  • Избегай вложенных блокировок или используй RLock
  • Для асинхронного кода используй asyncio.Lock вместо threading.Lock
  • В production используй Higher-level инструменты (Queue, Event, Condition)

Мьютексы — основа многопоточного программирования, но их необходимо использовать осторожно, чтобы избежать deadlock'ов и других проблем с синхронизацией.