Назад к вопросам
Middle
82
questionbank

Как можно синхронизировать взаимодействие между потоками?

Sobes Copilot

Получайте ответы в реальном времени

Ответ от нейросети

sobes.tech AI

Существует несколько основных механизмов синхронизации взаимодействия между потоками в C/C++:

  • Мьютексы (Mutexes): Обеспечивают эксклюзивный доступ к разделяемому ресурсу. Поток, захвативший мьютекс, блокирует другие потоки, пытающиеся его захватить, до тех пор, пока не освободит мьютекс.

    #include <mutex>
    
    std::mutex my_mutex;
    
    void my_thread_function() {
        my_mutex.lock(); // Захват мьютекса
        // Работа с разделяемым ресурсом
        my_mutex.unlock(); // Освобождение мьютекса
    }
    

    Или с использованием RAII:

    #include <mutex>
    #include <lock_guard>
    
    std::mutex my_mutex;
    
    void my_thread_function() {
        std::lock_guard<std::mutex> lock(my_mutex); // Захват мьютекса при создании объекта
        // Работа с разделяемым ресурсом
        // Мьютекс автоматически освободится при выходе из области видимости
    }
    
  • Северофоры (Semaphores): Обобщение мьютексов. Поддерживают счетчик, который позволяет заданному количеству потоков одновременно получить доступ к ресурсу.

    #include <semaphore.h> // Для POSIX-семафоров
    #include <windows.h> // Для Windows-семафоров
    
    sem_t my_semaphore; // POSIX-семафор
    
    void my_thread_function() {
        sem_wait(&my_semaphore); // Уменьшить счетчик семафора, заблокировать, если 0
        // Работа с ресурсом
        sem_post(&my_semaphore); // Увеличить счетчик семафора
    }
    

    (Примечание: Стандарт C++ не включает семафоры напрямую, но они доступны через системные библиотеки или сторонние реализации.)

  • Условные переменные (Condition Variables): Используются для того, чтобы поток мог ожидать наступления определенного условия, при этом освобождая мьютекс. Другой поток может оповестить ожидающий о выполнении условия.

    #include <mutex>
    #include <condition_variable>
    
    std::mutex my_mutex;
    std::condition_variable my_cv;
    bool condition_met = false;
    
    void waiting_thread() {
        std::unique_lock<std::mutex> lock(my_mutex);
        my_cv.wait(lock, []{ return condition_met; }); // Ждать выполнения условия
        // Условие выполнено, работа с ресурсом
    }
    
    void notifying_thread() {
        std::unique_lock<std::mutex> lock(my_mutex);
        condition_met = true;
        lock.unlock(); // Освободить мьютекс перед оповещением (опционально, но рекомендуется)
        my_cv.notify_one(); // Оповестить один ожидающий поток
        // или
        // my_cv.notify_all(); // Оповестить все ожидающие потоки
    }
    
  • Атомарные операции (Atomic Operations): Позволяют выполнять простые операции над переменными (например, инкремент, декремент) как единое, неделимое действие, не требующее явной блокировки мьютекс.

    #include <atomic>
    
    std::atomic<int> counter(0);
    
    void my_thread_function() {
        counter++; // Атомарный инкремент
        // Или:
        // counter.fetch_add(1);
    }
    
  • Read-Write Locks (Shared Mutexes): Позволяют нескольким потокам читать данные одновременно, но только одному потоку писать данные.

    #include <shared_mutex> // C++17 и выше
    
    std::shared_mutex my_shared_mutex;
    
    void read_thread() {
        my_shared_mutex.lock_shared(); // Захват для чтения
        // Чтение данных
        my_shared_mutex.unlock_shared(); // Освобождение для чтения
    }
    
    void write_thread() {
        my_shared_mutex.lock(); // Захват для записи (эксклюзивный)
        // Запись данных
        my_shared_mutex.unlock(); // Освобождение для записи
    }
    
  • Барьеры (Barriers): Позволяют группе потоков ожидать, пока все достигнуть определенной точки в своем выполнении, прежде чем продолжить.

    #include <barrier> // C++20
    
    std::barrier sync_point(4); // Барьер для 4 потоков
    
    void my_thread_function() {
        // Работа перед барьером
        sync_point.arrive_and_wait(); // Ожидание, пока все потоки достигнут барьера
        // Работа после барьера
    }
    

Выбор конкретного механизма зависит от характера взаимодействия и разделяемых ресурсов.