Чем отличаются мьютекс (Mutex) и семафор (Semaphore)

Ответ

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

Мьютекс (Mutex - Mutual Exclusion)

Назначение: Обеспечить взаимное исключение. Мьютекс гарантирует, что только один поток может получить доступ к критической секции кода или ресурсу в любой момент времени.

Принцип работы: Работает как двоичный замок (заблокирован/разблокирован). Если ресурс свободен, поток захватывает мьютекс и блокирует его. Другие потоки, пытающиеся захватить тот же мьютекс, будут заблокированы до тех пор, пока владелец его не освободит.

Ключевая особенность: Концепция "владения". Только тот поток, который захватил мьютекс, может его освободить.

from threading import Lock, Thread

# В Python мьютекс представлен классом Lock
lock = Lock()
shared_resource = 0

def task():
    global shared_resource
    for _ in range(100000):
        lock.acquire()  # Захват мьютекса
        try:
            shared_resource += 1 # Критическая секция
        finally:
            lock.release() # Освобождение мьютекса

# Запуск потоков...

Семафор (Semaphore)

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

Принцип работы: Работает как счетчик. При инициализации семафору задается начальное значение (например, 5), которое означает, что 5 потоков могут одновременно получить доступ к ресурсу. Каждый раз, когда поток захватывает семафор, счетчик уменьшается. Когда он освобождает — увеличивается. Если счетчик равен нулю, новые потоки блокируются.

Типы:

  • Счетный семафор (counting semaphore): счетчик > 1.
  • Двоичный семафор (binary semaphore): счетчик равен 1, по поведению очень похож на мьютекс.
from threading import Semaphore, Thread
import time

# Ограничиваем доступ к ресурсу тремя потоками одновременно
semaphore = Semaphore(3)

def access_resource(thread_id):
    print(f"Поток {thread_id} пытается получить доступ...")
    with semaphore: # Автоматический acquire/release
        print(f"Поток {thread_id} получил доступ!")
        time.sleep(2)
        print(f"Поток {thread_id} освобождает ресурс.")

# Запуск 5 потоков, но одновременно работать будут только 3
threads = [Thread(target=access_resource, args=(i,)) for i in range(5)]
for t in threads: t.start()

Сравнительная таблица

Критерий Мьютекс (Mutex) Семафор (Semaphore)
Назначение Взаимное исключение (защита ресурса) Ограничение доступа (контроль параллелизма)
Кол-во потоков Только один N (заданное число)
Владение Принадлежит потоку, который его захватил Не имеет концепции владения
Аналогия Ключ от одной туалетной комнаты Набор из N ключей от одинаковых душевых кабинок