Sailanarmo
Sailanarmo

Reputation: 1191

How to use std::async on a queue of vectors or a vector of vectors to sort?

I am trying to compare std::thread with std::async in this small example that I came up with for a fun exercise. I generate eight vectors with ten random numbers from 0 to 1000. I then push those vectors into a queue so that I can go ahead and multithread the sorting later. Here is the function I am using to generate the random numbers:

std::vector<int> generate(int count)
{
  std::vector<int> newVec(count);
  std::random_device rd;
  std::mt19937 mt(rd());
  std::uniform_int_distribution<> dis(0,1000);

  std::generate(begin(newVec),end(newVec),std::bind(dis,std::ref(mt)));

  return newVec;
}

In my thread example, I simply lock the queue, grab a vector out of it, call std::sort on it, and push that back into a list of vectors so I can merge it into one final vector in the end. That was easy enough to implement, but I am trying to figure out how to implement this same thing with std::async This is what I have tried so far:

void work(std::deque<std::vector<int>>& queue, std::list<std::vector<int>> toMerge)
{
  std::vector<int> temp;
  temp = queue.front();
  auto handle = std::async(std::launch::async,sortIt,temp);
  queue.pop_front();
  toMerge.push_back(temp);

}

I then realized, that this would not do what I thought it would. As I do not believe this could call itself over and over again. So I tried this one:

void work(std::deque<std::vector<int>>& queue, std::list<std::vector<int>> toMerge)
{
  if(queue.empty()
  {
    return;
  }
  else
  {
    auto handle = std::async(std::launch::async[&]{return std::sort(queue.front.begin(),queue.front.end());},work,queue,toMerge);
  }
}

But that gave me all sorts of compiler errors that I don't really know how to tackle.

How can I achieve this task?

Full code:

void sortIt(std::vector<int>& v)
{
  std::sort(begin(v),end(v));
}

void print(std::vector<int> &v)
{
  for(auto &&e : v)
  {
    std::cout << e << " ";
  }
  std::cout << std::endl;
}

std::vector<int> generate(int count)
{
  std::vector<int> newVec(count);
  std::random_device rd;
  std::mt19937 mt(rd());
  std::uniform_int_distribution<> dis(0,1000);

  std::generate(begin(newVec),end(newVec),std::bind(dis,std::ref(mt)));

  return newVec;
}

void work(std::deque<std::vector<int>>& queue, std::list<std::vector<int>> toMerge)
{
  //TODO: Make asnyc work
}

int main()
{
  std::deque<std::vector<int>> queue; 
  std::vector<int> tempA;
  std::vector<int> tempB;
  std::vector<int> finalVec;
  std::list<std::vector<int>> toMerge;

  for(int i = 0; i < 8; ++i)
  {
    queue.push_back(generate(10));
  }

  work(queue,toMerge);

}

Upvotes: 1

Views: 3725

Answers (1)

rafix07
rafix07

Reputation: 20936

All you need is to create vector for holding future objects, iterate over queue and at each iterator call async function to start asynchronous operation - in your case it is sorting of vector. For each future object you have to call get method to retrieve sorted vector. Obtained sorted vector by get is added to toMerge list.

void work(
  const std::deque<std::vector<int>>& queue, // const added - don't modify queue
  std::list<std::vector<int>>& toMerge) // pass toMerge by reference to store results
{
  std::vector<std::future< std::vector<int> >> tasks;
  for (const std::vector<int>& v : queue)
    tasks.push_back (std::async(
                                [vecCopy = v]() mutable { // copy v into vecCopy 
                                   std::sort(vecCopy.begin(), vecCopy.end()); 
                                   return vecCopy; 
                                }));

  // wait until all tasks are complete
  for (std::future< std::vector<int> >& f : tasks)
    toMerge.push_back(f.get()); // move vector into list
}

Lambda which sorts look like:

[vecCopy = v]() mutable { // copy v into vecCopy  
      std::sort(vecCopy.begin(), vecCopy.end()); 
      return vecCopy; 
}

you don't want to modify v from queue hence the copy of v is made, the content of v is copied into vecCopy. Because closure keeps vecCopy by value you need to put mutable keyword to lambda to allow vecCopy to be modified.

Upvotes: 2

Related Questions