You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++多线程:std::async阻塞、共享状态检查与future销毁问题

问题解答

首先贴出你用于实现旅行商问题的多线程递归代码:

typedef std::pair<std::vector<char>, int> PATH_PAIR;

std::list<PATH_PAIR>
TSP::return_all_paths_list(
    PATH_PAIR                                       parent_path_pair,
    std::list<char>                                 unvisited_cities
) {
    std::list<PATH_PAIR>                            subseq_paths_list;
    std::vector<std::future<std::list<PATH_PAIR>>>  future_vec;
   
    if (unvisited_cities.empty()) { // END RECURSION
        // NON-REALTED CODE
        return subseq_paths_list;
    }

    for (const char curr_city: unvisited_cities) {   // DO RECURSION
        PATH_PAIR                           new_path_pair_addr = parent_path_pair;
        std::list<char>                     subseq_unvisited_cities = unvisited_cities;
        std::future<std::list<PATH_PAIR>>   returned_path_list_future;
        // NON-RELATED CODE
        returned_path_list_future = std::move(std::async(std::launch::async, return_all_paths_list, this, new_path_pair_addr, subseq_unvisited_cities));

        future_vec.push_back(returned_path_list_future);
    }

    while (!future_vec.empty()) {   // GET-VALUES-WHILE
        for (std::future<std::list<PATH_PAIR>>& curr_future : future_vec)  // GET-VALUES-FOR
            if (curr_future.valid()) {
                subseq_paths_list.merge(curr_future.get());
            }
        // future_list.remove_if(
        //     [] (std::future<std::list<PATH_PAIR>> input_future) -> bool {
        //         return !input_future.valid();
        //     }
        // );
    }

    return subseq_paths_list;
}

1. 关于std::async的析构阻塞问题

a. "move from(移动)"和"bound to a reference(绑定到引用)"的含义

  • move from(移动):通过std::move()将std::async返回的临时std::future对象的所有权转移给另一个std::future变量。移动后原临时对象不再持有共享状态,其析构时不会触发阻塞。
  • bound to a reference(绑定到引用):将std::async返回的临时std::future绑定到右值引用(如std::future<T>&&),本质是避免临时对象在当前语句结束时直接析构,核心目的和移动语义一致——不让未被保存的临时std::future触发阻塞。

b. 使用std::move包裹std::async调用能否避免递归后续迭代阻塞?

可以。你代码中returned_path_list_future = std::move(std::async(...));的写法,将std::async返回的临时std::future所有权转移给了变量,随后又被移动到future_vec中,全程没有临时std::future在迭代过程中析构,因此不会触发后续迭代的阻塞。即使不显式写std::move,直接赋值给变量也能转移所有权,但显式移动的写法更清晰。

c. 示例中的阻塞是否仅因lambda表达式导致?

不是。你注释掉的remove_if lambda和阻塞问题无关。如果出现阻塞,大概率是某处std::async返回的临时std::future未被移动或保存,直接触发了析构阻塞。当前代码的写法不会因lambda导致阻塞,注释的lambda只是后续尝试清理容器的代码,和前置的阻塞场景无关。


2. 关于共享状态与std::future的疑问

a. 如何非阻塞检查共享状态是否ready?

使用std::future::wait_for()或std::future::wait_until()方法,传入零时长超时,通过返回值判断状态:

  • 返回std::future_status::ready:共享状态已就绪;
  • 返回std::future_status::timeout:共享状态未就绪;
  • 由于你用的是std::launch::async,不会出现std::future_status::deferred的情况。

示例代码:

if (curr_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
    subseq_paths_list.merge(curr_future.get());
}

b. 调用get()后如何正确销毁对应的std::future对象?

调用get()后,std::future的valid()会返回false,因为共享状态已被消耗。由于std::future不可拷贝,不能直接用remove_if,需用erase-remove惯用法配合引用传递的lambda:

future_vec.erase(
    std::remove_if(future_vec.begin(), future_vec.end(),
        [] (const std::future<std::list<PATH_PAIR>>& f) {
            return !f.valid();
        }
    ),
    future_vec.end()
);

你之前的编译错误是因为lambda用了值传递(std::future不可拷贝),改成引用传递即可解决。调用get()后容器大小不变,是因为你仅消耗了共享状态但未从容器中移除对应的std::future元素,执行上述erase-remove操作就能删除无效对象、更新容器大小。


内容的提问来源于stack exchange,提问作者nevermind

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 03:25:10