Как можно обмениваться информацией между потоками?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Механизмы обмена информацией между потоками в C++
Это одна из ключевых проблем многопоточного программирования. Существует несколько проверенных подходов.
1. Shared Memory + Синхронизация
Разделённая память с механизмами синхронизации — самый простой способ.
#include <thread>
#include <mutex>
#include <vector>
class ThreadSafeQueue {
private:
std::vector<int> data;
mutable std::mutex mtx;
public:
void push(int value) {
std::lock_guard<std::mutex> lock(mtx);
data.push_back(value);
}
bool pop(int& value) {
std::lock_guard<std::mutex> lock(mtx);
if (data.empty()) return false;
value = data.back();
data.pop_back();
return true;
}
};
Плюсы: просто, понятно, эффективно для малого количества данных. Минусы: блокировки замедляют работу при частых обращениях.
2. Condition Variables (Переменные условия)
Для синхронизации потоков по событиям. Один поток ждёт сигнала от другого.
#include <condition_variable>
#include <queue>
class ThreadSafeQueue {
private:
std::queue<int> data;
mutable std::mutex mtx;
std::condition_variable cv;
public:
void push(int value) {
{
std::lock_guard<std::mutex> lock(mtx);
data.push(value);
}
cv.notify_one();
}
int wait_and_pop() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this] { return !data.empty(); });
int value = data.front();
data.pop();
return value;
}
};
Плюсы: потоки не "заняты ожиданием". Минусы: сложнее в отладке, риск deadlock.
3. Atomic (Атомарные операции)
Для простых типов данных — безблокировочные и потокобезопасные операции.
#include <atomic>
class SimpleFlag {
private:
std::atomic<bool> flag{false};
std::atomic<int> counter{0};
public:
void set_flag() {
flag.store(true, std::memory_order_release);
}
bool get_flag() const {
return flag.load(std::memory_order_acquire);
}
void increment() {
counter.fetch_add(1, std::memory_order_relaxed);
}
};
Плюсы: очень быстро, нет блокировок. Минусы: только для простых типов, требует понимания memory_order.
4. Lock-Free структуры данных
Для высоконагруженных систем используются lock-free очереди без мьютексов.
#include <atomic>
template<typename T>
class LockFreeStack {
private:
struct Node {
T data;
Node* next;
};
std::atomic<Node*> head{nullptr};
public:
void push(const T& value) {
Node* new_node = new Node{value, nullptr};
Node* old_head = head.load(std::memory_order_acquire);
do {
new_node->next = old_head;
} while (!head.compare_exchange_weak(
old_head, new_node,
std::memory_order_release,
std::memory_order_acquire
));
}
};
Плюсы: максимальная производительность. Минусы: сложен для понимания.
5. Message Passing (Передача сообщений)
Вместо shared memory используются очереди сообщений. Часто в event-driven архитектурах.
#include <queue>
#include <functional>
class EventBus {
private:
std::queue<std::function<void()>> events;
std::mutex mtx;
public:
void post_event(std::function<void()> handler) {
std::lock_guard<std::mutex> lock(mtx);
events.push(handler);
}
void process_events() {
std::lock_guard<std::mutex> lock(mtx);
while (!events.empty()) {
events.front()();
events.pop();
}
}
};
Плюсы: слабая связанность потоков. Минусы: дополнительные накладные расходы на копирование.
Практический совет
Для 95% приложений начните с mutex + condition_variable. Оптимизируйте под lock-free только если профилирование показало узкое место. Используйте RAII для мьютексов, всегда проверяйте condition_variable в цикле, избегайте deadlock, тестируйте с ThreadSanitizer.