8

I want to call a method with async multiple times. A simplified example is shown below:

size_t counter(std::string &s)
{
    return s.size();
}

void stringCountAccumulator()
{
    std::vector<std::string> foos = {"this", "is", "spartaa"};
    size_t total = 0;
    for (std::string &s : foos)
    {
        std::future<size_t> fut = std::async(
            std::launch::async,
            counter, s);

        total += fut.get();
    }
    std::cout << "Total: " << total;
}

It seems that, fut.get() blocks other future calls. How can I implement this problem in C++? I need to call a function in a separate thread. This function "returns" a value.

Turkdogan Tasdelen
  • 898
  • 1
  • 11
  • 25

2 Answers2

9
void stringCountAccumulator()
{
  std::vector<std::string> foos = {"this", "is", "spartaa"};
  std::vector<std::future<size_t>> calcs;
  for (auto&& s : foos) {
    calcs.push_back( std::async(
      std::launch::async,
      counter, s)
    );
  }
  std::size_t total = 0;
  for (auto&& fut:calcs)
    total += fut.get();
  std::cout << "Total: " << total << "\n";
}

.get() is blocking. So don't block until you have queue'd up all tasks.

An alternative plan is to write/find a thread pool, and have each task update a possibly atomic (or mutex guarded) counter.

Have a finished-task counter guarded (again, possibly atomic).

Have a promise (of the total) which you fulfill when the last task is finished (fulfilled by the last task).

Return the future from that promise. Now you have a future representing the entire pool of threads calculating their value and adding it up, with lots of concurrency.

Some frameworks, like microsoft's ppl, have a system where it does something like this for you; you have tasks that return values, and a function object that combines the values, and get the result of the combination out of it.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Yakk, I have another question. If there is a global variable which is updated in counter function, does it have any problems ? Like something that `size_t counter(std::string &s, std::vector &listSize) { listSize.push_pack(s.size()); return s.size(); }` – fanfan1609 Sep 04 '19 at 02:19
  • 1
    @fanfan Please use the [ask question] button to ask questions on stack overflow. – Yakk - Adam Nevraumont Sep 04 '19 at 03:09
1

You also need to declare that StringCountAccumulator() will be executed asynchronously. Also call future::get() only when the future is ready. Here's a code snippet :

 std::future<void> stringCountAccumulator()
 {
    std::vector<std::string> foos = {"this", "is", "spartaa"};
    size_t total = 0;
    for (std::string &s : foos)
    {
        std::future<size_t> fut = std::async(
           std::launch::async, counter, s);
        while (!fut.is_ready() ) ;
        total += fut.get();
    }
   std::cout << "Total: " << total;
  }
ark1974
  • 615
  • 5
  • 16
  • This is supposed to return a `future`, but where is the return statement? – smac89 Mar 06 '18 at 10:51
  • @smac89 I can't find anything specific but it seems that this is [correct and intended](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#cp60-use-a-future-to-return-a-value-from-a-concurrent-task). It is indicating that this function is asynchronous and that the value isn't returned. Hence the void as the template parameter. – Tarick Welling Jun 21 '20 at 09:24