Ответ
Мьютекс и семафор — это примитивы синхронизации с разной семантикой.
Мьютекс (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++ они используются по-разному.
Ответ 18+ 🔞
А, ну вот, опять про эти мьютексы и семафоры. Слушай, давай разложу по полочкам, а то народ путается, как будто в первый раз слышит.
Представь себе, блядь, туалет в офисе. Один унитаз. Мьютекс — это как защёлка на двери изнутри. Зашёл, щёлк — заперся. Пока ты там не сделаешь свои дела и не откроешь, никто другой не влезет. Владение строгое: кто закрыл, тот и открывает. Идеально, чтобы защитить свою жопу, то есть критическую секцию кода, от посторонних глаз. В С++ это std::mutex, а lock_guard — это такой автомат, который щёлкает за тебя: вошёл — запер, вышел — сам отпер. Удобно, ёпта.
std::mutex g_mutex;
int shared_data = 0;
void safe_increment() {
std::lock_guard<std::mutex> lock(g_mutex); // Вошёл в кабинку — щёлк!
++shared_data;
}
А теперь семафор — это уже не один унитаз, а, допустим, три кабинки в общем туалете. Есть счётчик свободных мест. std::counting_semaphore<10> — это как табличка «свободно: 3». Зашёл ты (acquire) — счётчик уменьшился. Может, за тобой зайдёт твой кореш и тоже займёт кабинку. А выйдет (release) сначала он, а потом ты. Или наоборот. Владения нет, главное — чтобы счётчик не ушёл в минус. Это ж для управления пулом ресурсов, типа подключений к базе. Овердохуища потоков могут работать, но одновременно — только N штук.
// Пусть будет 3 подключения к базе, больше не выдержит
std::counting_semaphore<10> db_semaphore(3);
void query_database(int query_id) {
db_semaphore.acquire(); // Ждём, пока не освободится хоть одна кабинка-коннект
// ... делаем запрос ...
db_semaphore.release(); // Отпустили, пусть другие пользуются
}
Так в чём, блядь, разница-то, спросишь ты? А разница пиздец принципиальная.
- Счётчик. Мьютекс — он как девственница, бинарный: либо занят (1), либо свободен (0). Семафор — шлюха опытная, у него счётчик от 0 до N. Может обслуживать несколько потоков сразу, если N > 1.
- Владение. Мьютекс — хитрая жопа, требует, чтобы отпускал тот же, кто и взял. Семафору похуй: один потокищщщ взял, другой — отпустил. Как в том анекдоте про «мужик входит в бар...». Это для паттернов типа «производитель-потребитель» — самое то.
- Для чего. Мьютекс — чтобы данные не ебнулись, когда к ним одновременно лезут. Семафор — чтобы доступ к ресурсам (тем же потокам, коннектам, принтерам) ограничить.
Аналог простой: мьютекс — это вырожденный случай семафора, у которого счётчик максимум 1. Но из-за этой своей привередливой семантики владения в С++ их используют по-разному. Не путай, а то будет тебе хиросима в коде, чувак.