我在理解 C++11 promises、futures 以及它们如何与不同上下文交互时遇到了一些困难。
总的来说,我的目标是让程序在计算线程中生成值并在主线程中打印它们。在主线程获取生成值之前,我想拦截并更改它。在底部的示例代码中,拦截了一个值为 asdf
的 future ,并在前面添加了 redirect:
,将 redirect:asdf
返回给 future 。
使用 LLVM 9、GCC 5/6/7 或 Visual C++ 19 编译此代码工作正常。然而,在抛出奇怪错误的同时,所有在 lambda 中的 f.get()
都崩溃了。例如,在 MacOS 上使用 LLVM (LLDB) 进行调试会从 futures 库深处的某处得到 EXC_BAD_ACCESS (code=1, address=0x18)
,然后退出代码 11(段错误)。我认为这不是库实现的问题,因为它在所有编译器上的行为都相同。
我发现有几种方法可以消除错误,但代码不在我想要的结构中。一种是从 push_redirect
中简单地 return f;
,丢弃异步内容而不改变 future 的值。另一种是从 main
调用 push_new
而不是 push_redirect
,同样不改变 future 的值。归根结底,我希望能够根据需要堆叠尽可能多的 future 重定向。
我做的有什么特别错误的吗?我怀疑这可能与 lambda 的按引用捕获有关,但我不知道如何安排代码以避免在不使用全局变量的情况下按引用捕获。这也可能与范围有关。
下面是一个最小的示例,是从出现此错误的较大程序中剥离出来的。它应该在任何 online 上编译或可以处理 C++11 或更好的离线 C++ 编译器。
#include <string>
#include <iostream>
#include <future>
#include <queue>
struct PromiseContainer {
std::promise<std::string> p;
};
std::queue<PromiseContainer *> q;
void other_thread()
{
std::string str("abcd");
while (true) {
while (q.empty());
auto pc = q.front();
q.pop();
if (pc == nullptr) break;
else {
pc->p.set_value(str);
delete pc;
}
}
}
std::future<std::string> push_new()
{
auto p = std::promise<std::string>();
auto f = p.get_future();
auto pc = new PromiseContainer();
pc->p = std::move(p);
q.push(pc);
return f;
}
std::future<std::string> push_redirect()
{
auto f = push_new();
return std::async(std::launch::deferred, [&]()->std::string {
return "redirect:" + f.get();
});
}
int main()
{
auto t = std::thread(other_thread);
auto f = push_redirect();
q.push((PromiseContainer *) nullptr);
f.wait();
std::cout << f.get() << std::endl;
t.join();
}
最佳答案
push_redirect
中的
f
是局部变量,因此您的 lambda(带 &)
[&]()->std::string {
return "redirect:" + f.get();
});
持有对这个变量的引用,当 push_redirect
结束时 f
被删除并且你得到未定义的行为 - 异步创建的线程想要读取被销毁的数据。
如果您使用的是 C++14,您可以在 lambda 的捕获列表中移动 f
future 对象:
std::future<std::string> push_redirect()
{
auto f = push_new();
return std::async(std::launch::deferred, [f = std::move(f)]() mutable ->std::string {
return "redirect:" + f.get();
});
}
您还应该使用互斥来同步对您的q
队列的访问。
关于c++ - 使用 C++ future 作为函数堆栈中的中间值会导致段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49124568/