【问题标题】:Variable number of async threads with C++11使用 C++11 的可变数量的异步线程
【发布时间】:2015-01-13 21:44:34
【问题描述】:

我正在开发一个我想在循环中使用异步的程序。在我包含的示例代码中,只有 10 个元素,因此我可以轻松地为每个元素创建一个显式变量。但是,在我的主程序中,向量中的元素数量可能会有所不同。理想情况下,我想创建一个异步线程向量 - 数组中的每个元素一个 - 在我循环时被推回异步向量。然后我想等待它们全部完成,然后使用“ get()" 返回所有输出。

下面的代码将通过为每个线程分配一个显式变量来调用 async,但是有谁知道如何在向量中动态调用 async 而不必显式地为其分配一个变量?理想情况下,我希望这个程序在每次循环时调用一次“std::cout”,而不是只调用一次。

#include <iostream>
#include <vector>
#include <string>
#include <future>

std::string hi (std::string input)
{
    return "hello, this is " + input;
}

int main()
{
    std::vector<std::string> test_vector( 10, "a test" );
    std::future<std::string> a;
    std::future<std::string> b;

    for ( int i = 0; i < test_vector.size ( ); i++ )
    {
        a = std::async(std::launch::async, hi, test_vector[i]);
    }

    std::cout << a.get() << std::endl;

    return 0;
 }

【问题讨论】:

  • 你的问题到底是什么?
  • 如何在每次循环通过时动态调用异步,而不必显式创建变量。
  • 上面写的代码有一个严重的缺陷,每次你为a分配一个新的未来时,你强制它阻塞并等待与其旧值关联的线程完成,所以你'重新有效地串行运行。无论如何,我很惊讶动态数量的期货的解决方案并不完全显而易见:将每个新的未来存储在一个动态大小的容器中,就像一个向量。您甚至自己说过:“我想创建一个异步线程向量 - 数组中的每个元素一个 - 在我循环时这些线程被推回异步向量。” 那么为什么你不是刚做的吗!
  • 矢量是我描述它的最佳方式,我尝试过使用这种东西但没有任何加速,想看看其他人的建议,虽然这似乎是唯一的这样做的方法,我的程序没有运行得更快。
  • 也许异步函数会递归调用自己?我不知道这是否可能,你将不得不等待调用第一个函数的开销,但是一旦你有两个运行,这两个将调用另外两个,它会递归增长。也许这就是你正在寻找的。使用互斥锁设置一个全局变量,以便线程可以看到调用了多少线程并中断递归。

标签: c++ multithreading c++11 concurrency


【解决方案1】:

如果我理解正确,可能是这样的:

std::vector<std::future<std::string>> vessel;
for ( int i = 0; i < test_vector.size ( ); i++ )
{
    std::future<std::string> aux;
    aux = std::async(std::launch::async, hi);
    vessel.push_back(aux);
}

抱歉,我不能作为评论发表,但这种方式取决于逻辑,如果它有效,那么您应该能够动态操作矢量vessel


更新

更好:

vessel.push_back(new std::future<std::string>);
vessel[vessel.size()-1] = std::async(std::launch::async, hi);

这样你就不需要显式声明一个变量。但是你必须在完成后delete

for(int i=0; i<(int) vessel.size(); i++)
{
    delete vessel[i];
}

【讨论】:

  • CPlusPlus unique_ptr reference,声明unique_ptr objects automatically delete the object they manage (using a deleter) as soon as they themselves are destroyed。感谢您指出@0x499602D2。这个example 说明了应该如何使用它。
  • 最好不要使用原始指针(异常安全),更好的是:根本不要使用指针。正如 Galiks 的回答所示,不使用它们是完全合理的。减少间接性是个好主意(尽管这里的成本可能可以忽略不计)。
  • 即使忽略您的“更好”版本无法编译的事实(您不能将std::future 分配给std::future*),也没有任何理由动态分配期货。 vessel.push_back(std::async(std::launch::async, hi) 工作完美,更安全,并且至少与执行更少的分配和指针间接一样高效。
  • 我会更新答案。我应该测试过它。我会在这里帮助人们的窍门。就在最近,我在大学完成了一门 OOP 课程,我正在努力好好利用它。
【解决方案2】:

答案包括std::cout

std::vector<std::future<std::string>> a;
for (int i = 0; i < 10; ++i) {
  a.emplace_back(std::async(hi));
}
for (auto& element : a) {
  std::cout << element.get() << std::endl;
}

【讨论】:

    【解决方案3】:

    您可以通过创建一个期货向量来匹配您的线程向量来解决这个问题,如下所示:

    #include <iostream>
    #include <vector>
    #include <string>
    #include <future>
    
    std::string hi(const std::string& input)
    {
        return "hello, this is " + input;
    }
    
    int main()
    {
        std::vector<std::string> tests = {"one", "two", "three", "four"};
        std::vector<std::future<std::string>> futures;
    
        // add the futures to the futures vector as you launch
        // your asynchronous functions
        for(auto&& t: tests)
            futures.emplace_back(std::async(std::launch::async, hi, std::cref(t)));
    
        // collect your results
        for(auto&& f: futures)
            std::cout << f.get() << '\n';
    }
    

    注意使用std::cref 来传递const 引用。使用std::ref 传递非 const 引用

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-31
    • 1970-01-01
    • 1970-01-01
    • 2016-07-20
    • 1970-01-01
    • 1970-01-01
    • 2017-09-13
    • 2021-11-22
    相关资源
    最近更新 更多