繁体   English   中英

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

[英]Using a C++ future as an intermediate value in a function stack results in a segfault

我在理解C ++ 11的承诺,未来以及它们如何与不同的上下文进行交互时遇到了一些麻烦。

总的来说,我的目标是拥有一个程序,该程序在计算线程中生成值并将其打印在主线程中。 在主线程获取生成的值之前,我想对其进行拦截和更改。 在底部的示例代码,带值的未来asdf被拦截并redirect:被预置,返回redirect:asdf未来。

使用LLVM 9,GCC 5/6/7或Visual C ++ 19编译此代码可以正常工作。 但是,所有这些f.get()在lambda中炸毁f.get() ,同时f.get()奇怪的错误。 例如,在MacOS上使用LLVM(LLDB)进行调试可以从期货库中的某个深处获得EXC_BAD_ACCESS (code=1, address=0x18) ,然后发出退出代码11(段错误)的声音。 我认为这不是库实现的问题,因为它在所有编译器上的行为都相同。

我发现有几种方法可以使错误消失,但随后代码不在我想要的结构中。 一种是简单地return f; push_redirect ,丢弃异步内容并且不更改future的值。 另一种是从main而不是push_redirect调用push_new ,也不会更改future的值。 总而言之,我希望能够根据需要堆叠尽可能多的将来重定向。

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

下面是一个最小的示例,从显示此错误的较大程序中删除了该示例。 它应该在可以处理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可以移动f在lambda的捕获列表未来的对象:

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队列的访问。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM