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

В чем разница между Mutex и Semaphore?

2.3 Middle🔥 81 комментариев
#Многопоточность и асинхронность

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

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

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

Разница между Mutex и Semaphore

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

Ключевые отличия

1. Назначение

  • Mutex: Используется для взаимоисключающего доступа к ресурсу. Гарантирует, что критическая секция выполняется только одним потоком.
  • Semaphore: Используется для ограничения доступа к ресурсу определенным числом потоков. Может разрешать доступ нескольким потокам одновременно.

2. Владение

  • Mutex: Имеет концепцию владения (ownership). Только поток, который захватил мьютекс, может его освободить.
  • Semaphore: Не имеет владения. Любой поток может увеличить (освободить) или уменьшить (захватить) счетчик семафора.

3. Счетчик

  • Mutex: По сути, это бинарный семафор со счетчиком 0 или 1, но с дополнительной логикой владения.
  • Semaphore: Счетчик может быть неотрицательным целым числом, задаваемым при инициализации.

4. Производительность

В большинстве реализаций Mutex более легковесен и оптимизирован для случая взаимного исключения, в то время как Semaphore может быть более тяжелым из-за поддержки счетчика.

Практические примеры

Пример с Mutex в Swift

import Foundation

class ThreadSafeCounter {
    private var count = 0
    private let mutex = NSLock() // Mutex
    
    func increment() {
        mutex.lock()
        count += 1
        mutex.unlock()
    }
    
    func getValue() -> Int {
        mutex.lock()
        defer { mutex.unlock() }
        return count
    }
}

// Использование
let counter = ThreadSafeCounter()
DispatchQueue.concurrentPerform(iterations: 100) { _ in
    counter.increment()
}
print(counter.getValue()) // Всегда 100

Пример с Semaphore в Swift

import Foundation

class ResourcePool {
    private let semaphore: DispatchSemaphore
    
    init(poolSize: Int) {
        semaphore = DispatchSemaphore(value: poolSize)
    }
    
    func useResource(identifier: String) {
        semaphore.wait() // Уменьшаем счетчик
        print("Ресурс используется: \(identifier)")
        Thread.sleep(forTimeInterval: 1) // Имитация работы
        print("Ресурс освобожден: \(identifier)")
        semaphore.signal() // Увеличиваем счетчик
    }
}

// Использование
let pool = ResourcePool(poolSize: 3) // Одновременно могут работать 3 потока
for i in 1...10 {
    DispatchQueue.global().async {
        pool.useResource(identifier: "Task \(i)")
    }
}

Когда что использовать

Mutex стоит использовать:

  • Для защиты критических секций, где важен взаимоисключающий доступ
  • При работе с разделяемыми ресурсами (структуры данных, файлы)
  • Когда нужна гарантия, что освобождает блокировку тот же поток, что и захватил

Semaphore стоит использовать:

  • Для ограничения доступа к пулу ресурсов (например, подключения к БД)
  • При реализации производитель-потребитель (producer-consumer)
  • Для синхронизации работы нескольких потоков
  • Когда нужно ожидание события (семафор со счетчиком 0)

Особенности в iOS/macOS разработке

В iOS/macOS разработке чаще используются высокоуровневые механизмы:

  • DispatchQueue с barriers для взаимного исключения
  • OperationQueue с ограничением максимального количества операций
  • NSLock, NSRecursiveLock как реализации мьютексов
  • DispatchSemaphore для семафоров

Однако понимание низкоуровневых примитивов важно для:

  1. Оптимизации производительности в критических секциях
  2. Решения сложных задач синхронизации
  3. Понимания работы высокоуровневых абстракций
  4. Написания кросс-платформенного кода

Заключение

Выбор между Mutex и Semaphore зависит от конкретной задачи. Mutex идеален для защиты разделяемых ресурсов от одновременного доступа, а Semaphore — для управления доступом к ограниченному пулу ресурсов или синхронизации потоков. В iOS разработке обычно используются высокоуровневые API Grand Central Dispatch, но понимание этих фундаментальных примитивов необходимо для написания корректного многопоточного кода.