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

Какие знаешь примитивы синхронизации

1️⃣ Как кратко ответить

Примитивы синхронизации в C++ включают мьютексы (std::mutex), рекурсивные мьютексы (std::recursive_mutex), условные переменные (std::condition_variable), барьеры, атомарные операции (std::atomic), семафоры и спинлоки. Они используются для управления доступом к общим ресурсам в многопоточных приложениях.

2️⃣ Подробное объяснение темы

Примитивы синхронизации — это механизмы, которые позволяют управлять доступом к общим ресурсам в многопоточных приложениях, предотвращая состояние гонки и обеспечивая корректное выполнение программы. Рассмотрим основные примитивы синхронизации в C++.

Мьютексы (std::mutex)

Мьютекс (от англ. "mutual exclusion" — взаимное исключение) — это объект, который позволяет ограничить доступ к ресурсу так, чтобы в каждый момент времени только один поток мог его использовать.

#include <iostream>
#include <thread>
#include <mutex>
​
std::mutex mtx; // Создаем мьютекс
​
void print_message(const std::string& message) {
    mtx.lock(); // Захватываем мьютекс
    std::cout << message << std::endl; // Печатаем сообщение
    mtx.unlock(); // Освобождаем мьютекс
}
​
int main() {
    std::thread t1(print_message, "Hello from thread 1");
    std::thread t2(print_message, "Hello from thread 2");
​
    t1.join();
    t2.join();
​
    return 0;
}
  • std::mutex mtx; — объявление мьютекса.
  • mtx.lock(); — захват мьютекса, блокировка доступа к ресурсу.
  • mtx.unlock(); — освобождение мьютекса, разблокировка доступа.

Рекурсивные мьютексы (std::recursive_mutex)

Рекурсивный мьютекс позволяет одному и тому же потоку захватывать мьютекс несколько раз без блокировки. Это полезно, если функция вызывает саму себя и требует синхронизации.

#include <iostream>
#include <thread>
#include <mutex>
​
std::recursive_mutex rec_mtx;
​
void recursive_function(int count) {
    if (count <= 0) return;
    rec_mtx.lock();
    std::cout << "Count: " << count << std::endl;
    recursive_function(count - 1);
    rec_mtx.unlock();
}
​
int main() {
    std::thread t1(recursive_function, 5);
    t1.join();
​
    return 0;
}
  • std::recursive_mutex rec_mtx; — объявление рекурсивного мьютекса.
  • Позволяет одному потоку захватывать мьютекс несколько раз.

Условные переменные (std::condition_variable)

Условные переменные используются для блокировки потока до тех пор, пока не будет выполнено определенное условие.

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
​
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
​
void wait_for_ready() {
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, [] { return ready; });
    std::cout << "Thread is ready!" << std::endl;
}
​
void set_ready() {
    std::lock_guard<std::mutex> lock(mtx);
    ready = true;
    cv.notify_one();
}
​
int main() {
    std::thread t1(wait_for_ready);
    std::thread t2(set_ready);
​
    t1.join();
    t2.join();
​
    return 0;
}
  • std::condition_variable cv; — объявление условной переменной.
  • cv.wait(lock, [] { return ready; }); — блокировка потока до выполнения условия.
  • cv.notify_one(); — уведомление одного потока о выполнении условия.

Атомарные операции (std::atomic)

Атомарные операции позволяют выполнять операции над переменными без использования мьютексов, обеспечивая безопасность потоков.

#include <iostream>
#include <thread>
#include <atomic>
​
std::atomic<int> counter(0);
​
void increment() {
    for (int i = 0; i < 1000; ++i) {
        ++counter;
    }
}
​
int main() {
    std::thread t1(increment);
    std::thread t2(increment);
​
    t1.join();
    t2.join();
​
    std::cout << "Counter: " << counter << std::endl;
​
    return 0;
}
  • std::atomic<int> counter(0); — объявление атомарной переменной.
  • ++counter; — атомарное увеличение переменной.

Семафоры

Семафоры управляют доступом к ресурсу, позволяя ограниченному числу потоков использовать его одновременно.

Спинлоки

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

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

Тема: Многопоточность / Синхронизация
Стадия: Tech

🔒 Подпишись на бусти автора и стань Алигатором, чтобы получить полный доступ к функционалу сайта и отслеживать свой прогресс!

Твои заметки