如何检查线程的“共享状态”或“内存状态”的详细状态?如何不调用std::async( )块?

How to check the verbose status of a thread's "shared state" or "memory state"? How to not make calls to std::async( ) block?

提问人:nevermind 提问时间:7/23/2023 最后编辑:prapinnevermind 更新时间:7/27/2023 访问量:63

问:

我使用多线程的方法是:

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. 根据 cppreference std::async:

    如果从 std::async 获取的 std::future 未从引用中移动或绑定到引用,则 std::future 的析构函数将在完整表达式的末尾阻塞,直到异步操作完成,实质上使代码(如以下代码)同步:

    std::async(std::launch::async, []{ f(); }); // temporary's dtor waits for f()
    std::async(std::launch::async, []{ g(); }); // does not start until f() completes
    

    a. “移自”和“绑定到参考”这两个词是什么意思?
    b. 将调用包装在 an 中是否会使调用在循环的后续迭代中不阻塞?
    c. 或者上述两行中的调用是否仅仅因为使用了 lambda 而阻塞?
    std::asyncstd::movestd::async// DO RECURSION

  2. 如何检查共享状态是否已准备就绪
    a.a. 对 (和 ) 的调用会阻止,直到共享状态准备就绪。问题是,我不想等待。我想检查共享状态是否已准备就绪,如果没有,我想继续下一个对象。我该怎么做? 仅检查共享状态是否与对象关联
    b. 此外,根据 cppreference std::async,调用的返回值为:
    std::future::waitstd::future::getstd::futurestd::future::validstd::async

    std::future 是指此调用创建的共享状态std::async

    因此,在循环中,如果对它进行调用,则获取的将始终返回(前提是尚未对它进行调用)。如果我在调用 an 后删除,则不必检查对象的有效性。但是,我尝试过的删除元素 STL 方法都不起作用,编译时错误大约是显示器高度的两倍。//GET VALUESstd::futuretruestd::future::validstd::future::getstd::futurestd::future::getstd::future

    我想,也许调用除了释放共享状态之外还会破坏对象,但是我写了一段小测试代码:std::future::get

    int mul2(int x) {return x << 1; }
    int main() {
        std::vector<std::future<int>> fut_vec;
        for (int i = 0; i < 5; i++) {
            fut_vec.push_back(std::move(std::async(std::launch::async, mul2, i)));
        }
        for (std::vector<std::future<int>>::iterator itr = fut_vec.begin(); itr != fut_vec.end(); itr++) {
            if (itr->valid()) {
                std::cout << "Value: " << itr->get() << " | No. of vec elements(futures): " << fut_vec.size() << std::endl;
            }
        }
    
        return 0;
    }
    

    显示 总是返回相同的整数。fut_vec.size()

    如何销毁对象?std::future

我最近开始接触多线程,所以现在我正在尝试使用多线程来实现旅行推销员问题。

C++ 多线程 C++11

评论

0赞 273K 7/23/2023
std::future<T>::wait_forstd::future<T>::wait_until
0赞 Alan Birtles 7/23/2023
有关堆栈溢出的问题应仅包含一个问题。为什么尺寸会减小?您的代码中没有任何内容可以从中删除元素?fut_vec
0赞 nevermind 7/23/2023
@273K这些都是有时间限制的。此外,我不想等待。
0赞 nevermind 7/23/2023
@AlanBirtles 我的代码中没有任何内容可以删除元素,因为删除元素的方法(在本例中为)会导致编译时错误fut_vecstd::future
1赞 heap underrun 7/23/2023
然后你可以。如果出现错误,请发布完整的错误消息(在问题中,而不是在评论中)。通常,第一个错误是最相关的错误。wait_for(0s)

答:

0赞 Yakk - Adam Nevraumont 7/27/2023 #1

要检查未来的共享状态是否已准备就绪,请等待 0 秒。如果它准备好了,它就会这么说;否则,它将超时。

std 异步返回 future 的阻塞行为将传递给您将其移动到的任何位置。阻止行为处于共享状态,而不是返回的特定将来。

销毁对象与多线程问题无关。

你必须保留从 std async 返回的 futures,直到你知道任务已经完成,然后你可以销毁它们。如果你提前销毁它们,你将阻止任务完成。

通常,创建无限数量的并行线程对任何程序来说都是一个糟糕的计划。典型的并行代码会创建一个要处理的任务池,然后使用线程池来处理这些任务。

直接创建线程(或者通过 std::thread 或 std::async)并给它一个任务来处理,这有点像手动管理内存。当然,它可以解决问题,但这不是最佳实践。

...

我要做的是创建一个使用 和 的线程安全队列。添加方法、 。Tstd::condition_variablestd::mutexstd::dequepushpop

然后我会创建一个线程池。线程池拥有 的线程安全队列。它有一个方法。与未来不同的是,这个未来不会阻止毁灭。std::packaged_task<void>std::future<void> start_task( std::function<void()> )std::async

线程池维护 (hardware_concurrency_count) 个线程,无论是 还是已创建,每个线程都会从线程安全队列中弹出一个任务,执行该任务,然后重复。std::threadstd::async

也许在你的 中添加一个,以及一个 .wait_until_all_tasks_donethreadpoolterminate_all_tasks

您可以在 TSP 问题之外为线程安全队列和线程池编写单元测试,然后使用这两个基元解决 TSP,而不必同时处理有缺陷的线程代码。