问题描述
代码
#include <iostream>
#include <thread>
#include <atomic>
#include <future>
void listenForInput(std::promise<bool>&& interruptPromise)
{
std::cin.get();
std::atomic_thread_fence(std::memory_order_acquire);
interruptPromise.set_value(true);
std::cout << "[Speech 100] no u \n";
}
bool is_ready(std::future<bool> const& f)
{
return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
}
int main()
{
std::atomic_thread_fence(std::memory_order_acquire);
std::promise<bool> interruptPromise;
//interuptPromise.set_value(false);
/* Commenting line above makes program work. Uncommenting results in while
loop below not being executed */
std::atomic_thread_fence(std::memory_order_release);
std::future<bool> interruptFuture = interuptPromise.get_future();
std::thread reply(listenForInput,std::move(interruptPromise));
while (!is_ready(interruptFuture))
{
std::cout << "Your mother was a hamster,"
"and your father smelt of elderberries "
"(Press any key to reply)\n";
std::this_thread::sleep_for( std::chrono::seconds(2) );
}
reply.join();
std::cin.get();
return 0;
}
背景
在上面的代码中,主线程中的同一行文本不断显示,直到它被另一个线程中断。中断发生在用户的任何输入之后。用户进行输入的消息通过 std::promise
和 std::future
传递到主线程。 is_ready
是一个实用函数,用于检查 promise 是否得到满足。
我的兴趣在于 main()
函数的第三行及其周围的内容。起初,我试图将 interruptPromise
的值提前设置为 false(这表示中断没有发生,以便稍后在其他线程中将其更改为 true 以表明它确实发生了)。它导致 interruptPromise
在 while
循环甚至开始之前得到满足,因此循环没有被执行。
问题
- 我知道调用
std::promise
时set_value
会感到满意。但是我如何提前分配一个值,然后让std::future
对它的更改做出反应(例如future
会对false
bool
被更改为 {{1} })? - 我需要做这样的事情吗?
-
true
是否有某些类型的默认值(如果它需要的话)?
解决方法
Primise/future 不是一个会改变的价值,而是一个稍后交付的价值。
如果你想要一个改变的值,你可以将互斥锁、条件变量和布尔值组合在一起。然而,根据我的经验,推理一个在多线程环境中改变不止一次的布尔值会打破大多数人的大脑。
更好的选择是像上面保护的数据队列,尝试 pop 或尝试弹出所有从队列中取出数据的方法(如果存在)。
template<class T>
struct threadsafe_queue{
std::optional<T> try_pop();
std::optional<T> wait_and_pop();
bool is_aborted() const;
std::deque<T> pop_all();
void push(T);
void abort();
private:
std::condition_variable cv;
mutable std::mutex m;
std::deque<T> queue;
bool isAborted=false;
std::unique_lock<std::mutex> lock() const;
};
写上面,然后通过队列发送用户输入。主线程执行 try pop 并处理如果有任何结果。
std::optional<T> try_pop(){
auto l=lock();
if (is_aborted()||queue.empty()) return {};
auto r=queue.front();
queue.pop_front();
return r;
}
std::optional<T> wait_and_pop(){
auto l=lock();
cv.wait(l,[&]{return is_aborted()||!queue.empty();});
if (is_aborted()) return {};
auto r=queue.front();
queue.pop_front();
return r;
}
void push(T t){
auto l=lock();
if (is_aborted()) return;
queue.push_back(std::move(t));
cv.notify_one();
}
void abort(){
auto l=lock();
isAborted=true;
cv.notify_all();
}
有一些示例实现。