c++ - 使用 C++ future 作为函数堆栈中的中间值会导致段错误

标签 c++ c++11 promise future

我在理解 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/

相关文章:

c++ - 计数排序无限循环

c++ - 为什么 basic_stringbuf 和 basic_filebuf 移动构造函数具有实现定义的行为?

c++ - 关于 vector 元素的弹出

c++ - 使用 vector 作为多线程队列是否安全?

c++ - 如何正确地将 C 字符串传递给可变参数模板函数内的 lambda

jquery - $.each 使用 Promise 完成后运行 jquery 函数

javascript - 读取多个目录中的文件,使用 Node 和 Promises 将文件名与其数据匹配

c++ - 在 C++ 中打印汉字

c++ - constexpr和const之间的区别

javascript - 在node.js中迭代大量异步调用/结果(使用ES6/async/bluebird/generators)?