Как можно синхронизировать взаимодействие между потоками?
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(); // Ожидание, пока все потоки достигнут барьера // Работа после барьера }
Выбор конкретного механизма зависит от характера взаимодействия и разделяемых ресурсов.