#c #c 11 #promise #future #condition-variable
#c #c 11 #обещание #будущее #условие-переменная
Вопрос:
У меня есть два потока. Один поток действует как поток таймера, который через регулярные промежутки времени должен отправлять уведомления другому потоку. Я планирую использовать C Promise и Future для этой цели вместо переменных условий C .
У меня есть следующие ограничения / условия :-
- Хотелось бы избежать блокировки мьютекса (поэтому решил не использовать C
std::condition_variable
для этого, нужно использовать мьютекс) - Потоку таймера (или уведомляющему) не нужно отправлять уведомление потребителю, если он еще не готов (т. Е. Все еще Действует на последнее уведомление)
Я решил использовать C Promise и Future и придумал этот фрагмент кода.
// promiseFutureAtomic.cpp
#include <atomic>
#include <condition_variable>
#include <iostream>
#include <thread>
#include <iostream> // std::cout, std::endl
#include <thread> // std::this_thread::sleep_for
#include <chrono> // std::chrono::seconds
#include <future>
std::promise<void> aPromisingFuture;
std::atomic<bool> readyForNotification{false};
std::atomic<bool> stop_consumer{false};
void waitingForWork(){
while (!stop_consumer)
{
std::cout << "Waiting " << std::endl;
std::promise<void> newPromise;
aPromisingFuture = std::move(newPromise);
auto aFuture = aPromisingFuture.get_future();
readyForNotification = true;
aFuture.wait();
std::cout << "Running " << std::endl;
// Do useful work but no critical section.
}
}
void setDataReady(){
int i = 0;
while (i < 10)
{
std::this_thread::sleep_for (std::chrono::seconds(1));
if (readyForNotification)
{
readyForNotification = false;
std::cout << "Data prepared" << std::endl;
aPromisingFuture.set_value();
}
}
stop_consumer = true;
}
int main(){
std::cout << std::endl;
std::thread t1(waitingForWork);
std::thread t2(setDataReady);
t1.join();
t2.join();
std::cout << std::endl;
}
Мой вопрос :-
- Как производительность этого подхода будет сравниваться с предстоящим C 20
std::atomic_wait
/std::atomic_notify
? std::future<T>::wait()
Страдает ли от ложных пробуждений (например, Cstd::condition_variable::wait
)?- Есть ли какие-либо недостатки этого подхода по сравнению с обычным подходом
std::condition_variable::wait
?
Комментарии:
1. @EOF —
std::this_thread::sleep_for
там нет, чтобы избежать мьютекса. Он предназначен только для имитации таймера (если вы прочитали вопрос, в нем говорится, что один поток действует как таймер, детали таймера в этом контексте не имеют значения, и режим сна просто имитирует это.) С отключением режима сна, чем этоstd::atomic<bool>
хуже, чем мьютекс?
Ответ №1:
Условие гонки в примере кода
В приведенном выше примере есть условие гонки. Если setDataReady() устанавливает для stop_consumer значение true после того, как функция waitingForWork() проверила условие !stop_consumer, но до того, как она вызвала функцию aFuture.wait(), то функция waitingForWork() будет ждать завершения.
Это можно преодолеть с помощью std::future::wait_for() , который может по таймауту, позволяя повторную проверку stop_consumer . Я ценю, что в простом примере кода вы можете не захотеть загромождать его дополнительным кодом для перепроверки.
Как Windows, так и Linux поддерживают ожидание нескольких дескрипторов с помощью WaitForMultipleObjects() и poll() / epoll() . Используя эти собственные методы, уведомление потока может выполняться с ожиданием, которое не требует тайм-аута. Вместо этого код может ожидать уведомления либо обычного дескриптора, либо дескриптора завершения работы. Я не сталкивался с поддержкой стандартной библиотеки для этого.
Ложные пробуждения
В пункте 39 Effective Modern C Скотт Мейерс обсуждает то, что он называет void futures . Это уведомления, подобные приведенным выше, хотя и с одним выстрелом. Он перечисляет отсутствие ложных пробуждений как одну из причин предпочесть future / promise переменным условий.
Другие преимущества future / promise
Мейерс перечисляет несколько других причин, по которым он предпочитает future / promise вместо condition_variable:
- Запах кода мьютекса, необходимого для сопровождения condition_variable .
- Необходимость избегать уведомления переменной условия до того, как потребляющий поток ожидает condition_variable .
Производительность future / promise по сравнению с std::atomic_notify
Я не смотрел, одна из причин, по которой я подозреваю, что производительность больше зависит от переключения контекста и планирования потоков, чем от чистого уведомления. Даже если уведомление было значительным, это могло зависеть от реализации.
Другие мысли
Приведенный выше пример приведен в качестве простого примера. В реальном использовании, в чем-то отличном от небольшого приложения, я бы обернул код периодического уведомления и избегал использования глобальных значений.