簡體   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