C++ 使用C++;future作为函数堆栈中的中间值会导致segfault

C++ 使用C++;future作为函数堆栈中的中间值会导致segfault,c++,c++11,promise,future,C++,C++11,Promise,Future,我在理解C++11承诺、未来以及它们如何与不同上下文交互方面遇到了一些困难 总的来说,我的目标是有一个在计算线程中生成值并在主线程中打印它们的程序。在主线程获取生成的值之前,我想截取并更改它。在底部的示例代码中,拦截值为asdf的未来,并在redirect:前面加上前缀,返回redirect:asdf到未来 编译此代码与LLVM 9、GCC 5/6/7或Visual C++ 19工作良好。但是,在lambda中的f.get()上,所有这些都会爆炸,同时抛出奇怪的错误。例如,在MacOS上使用LL

我在理解C++11承诺、未来以及它们如何与不同上下文交互方面遇到了一些困难

总的来说,我的目标是有一个在计算线程中生成值并在主线程中打印它们的程序。在主线程获取生成的值之前,我想截取并更改它。在底部的示例代码中,拦截值为
asdf
的未来,并在
redirect:
前面加上前缀,返回
redirect:asdf
到未来

编译此代码与LLVM 9、GCC 5/6/7或Visual C++ 19工作良好。但是,在lambda中的

f.get()
上,所有这些都会爆炸,同时抛出奇怪的错误。例如,在MacOS上使用LLVM(LLDB)进行调试时,可以从futures库的某个深处进行
EXC\u BAD\u访问(code=1,address=0x18)
,然后使用退出代码11(segfault)发出咯咯声。我不认为这是库实现的问题,因为它在所有编译器上的行为都是相同的

我发现有几种方法可以消除错误,但是代码不在我想要的结构中。一种是简单地
返回f
push\u redirect
,丢弃异步内容,不更改未来的值。另一种是从
main
调用
push\u new
,而不是
push\u redirect
,也不会更改未来的值。在一天结束时,我希望能够堆叠尽可能多的未来重定向,因为我想

我做的有什么特别错误的吗?我怀疑这可能与lambda的引用捕获有关,但我不知道如何安排代码以避免在不使用全局变量的情况下通过引用捕获。这也可能与范围有关

下面是一个最小的例子,从显示此错误的较大程序中剥离出来。它应该编译在任何或离线的C++编译器上,它可以处理C++ 11或更好。
#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();
}
#包括
#包括
#包括
#包括
结构承诺容器{
std::promise p;
};
std::队列q;
无效其他_线程()
{
std::string str(“abcd”);
while(true){
而(q.empty());
自动pc=q.前();
q、 pop();
如果(pc==nullptr)中断;
否则{
pc->p.设定值(str);
删除pc;
}
}
}
标准::未来推送新()
{
自动p=std::promise();
自动f=p。获取未来();
自动pc=新的PromiseContainer();
pc->p=std::move(p);
q、 推送(pc);
返回f;
}
std::future push_redirect()
{
自动f=推新();
返回std::async(std::launch::deferred,[&]()->std::string{
return“redirect:+f.get();
});
}
int main()
{
自动t=标准::线程(其他线程);
自动f=推送_重定向();
q、 推送((承诺容器*)为空;
f、 等待();

std::cout
f
push\u redirect
中是局部变量,因此您的lambda(带&)

push\u redirect
结束
f
被删除时,保持对该变量的引用,并且您得到未定义的行为-由async创建的线程希望读取已销毁的数据

如果您使用的是C++14,则可以在lambda的捕获列表中移动
f
未来对象:

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();
  });
}
std::future push_redirect()
{
自动f=推新();
返回std::async(std::launch::deferred,[f=std::move(f)]()可变->std::string{
return“redirect:+f.get();
});
}

你也应该使用互斥来同步对
q
队列的访问。

你的队列上有一个竞争条件。如果没有某种同步,你不能从两个不同的线程读写。还有为什么
PromiseContainer
?是的,我应该更好地解释一下。队列实际上很好-在这个例子中是ish。我从中提取的代码确实使用了访问同步。我在上面的代码中省略了它,以关注主要问题(这在
push\u redirect
中是一个范围问题).PromiseContainer
之所以存在,是因为我想像我开始的代码那样构造我的演示代码。C++14是可以接受的,您确实解决了我的问题。感谢您的解释。我不知道您可以在捕获列表中正确移动futures。是否有相关文档?在lambda ca部分pture,这里有一些关于捕获初始值设定项的示例。啊,我现在看到了。谢谢。关于您提到的同步,我开始使用的代码使用了互斥和条件变量,而不是繁忙的等待循环。我在发布的代码中忽略了它,以关注范围错误。
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();
  });
}