Ответ
Мьютекс и семафор — это примитивы синхронизации с разной семантикой.
Мьютекс (mutex, mutual exclusion):
- Назначение: Обеспечить взаимное исключение для доступа к общему ресурсу. В один момент времени мьютексом может владеть только один поток.
- Владение: Мьютекс должен быть освобожден тем же потоком, который его захватил. Это делает его идеальным для защиты критических секций.
- В C++:
std::mutex,std::timed_mutex,std::recursive_mutex. Используется вместе сstd::lock_guardилиstd::unique_lockдля автоматического управления временем жизни.
#include <mutex>
#include <thread>
#include <iostream>
std::mutex g_mutex;
int shared_data = 0;
void safe_increment() {
std::lock_guard<std::mutex> lock(g_mutex); // Захват при создании, освобождение при разрушении
++shared_data;
std::cout << "Thread " << std::this_thread::get_id()
<< ": shared_data = " << shared_data << 'n';
}
Семафор (semaphore):
- Назначение: Ограничить доступ к ресурсу (или пулу ресурсов) N потокам одновременно. Семафор управляет внутренним счетчиком.
- Владение: Поток, который уменьшил счетчик (
acquire), не обязан быть тем же, который его увеличивает (release). Это позволяет реализовать шаблоны типа "производитель-потребитель". - В C++:
std::counting_semaphore(C++20). Имитирует доступ к ограниченному количеству ресурсов.
#include <semaphore>
#include <thread>
#include <iostream>
#include <vector>
// Допустим, у нас есть пул из 3 подключений к БД
std::counting_semaphore<10> db_connection_semaphore(3); // Максимум 3, изначально 3 свободны
void query_database(int query_id) {
db_connection_semaphore.acquire(); // Ждет, если свободных подключений нет
// Симуляция работы с БД
std::cout << "Query " << query_id << " started...n";
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << "Query " << query_id << " finished.n";
db_connection_semaphore.release(); // Освобождает подключение
}
Ключевые различия:
- Счетчик: Мьютекс по сути бинарный (0 или 1). Семафор имеет счетчик (0..N).
- Владение: У мьютекса есть понятие владельца, у семафора — нет.
- Использование: Мьютекс защищает данные от одновременного доступа. Семафор управляет доступом к ресурсам (например, к пулу потоков, подключениям).
Аналог: Мьютекс — это частный случай семафора с максимальным счетчиком, равным 1. Однако из-за семантики владения в C++ они используются по-разному.