Reputation: 205
I'm working on a multithreaded C++ vapply function, which applies a function to each element of a vector. The trick is that I don't want to create a thread for each element in the vector; I would rather create a set number of threads and give them each a piece of the vector to work on.
I'm not sure how to capture the result of the operation using std::async
, however. Here's what I have so far:
vapply.h
:
#ifndef VAPPLY_H
#define VAPPLY_H
#include <vector>
#include <thread>
#include <memory>
#include <future>
#include <functional>
class vapply
{
private:
template <class Fn, class T>
auto docall(Fn&& fn, std::vector <T> &v, size_t begin, size_t end)
{
using return_type = typename std::result_of <Fn(T)>::type;
std::vector <return_type> result;
result.reserve(end - begin);
for (size_t i = begin; i < end; i++)
{
result.emplace_back(fn(v[i]));
}
return result;
}
public:
// Constructor
template <class Fn, class T>
vapply(Fn&& fn, std::vector <T> &v)
{
size_t maxNumThreads = std::thread::hardware_concurrency() - 1;
size_t funcPerThread = v.size() / maxNumThreads;
size_t funcModThread = v.size() % maxNumThreads;
size_t funcToPerform = 0;
for (size_t i = 0; i < v.size(); i += funcToPerform)
{
funcToPerform = (i == 0) ? funcPerThread + funcModThread : funcPerThread;
// this line works fine, extract the results of docall
auto p = docall(std::forward <Fn>(fn), v, i, i + funcToPerform);
// now I'd like to do the same thing but in a separate thread, but below doesn't compile
// auto q = std::async(std::launch::async, &vapply::docall, std::forward <Fn>(fn), v, i, i + funcToPerform);
}
}
};
#endif /* VAPPLY_H */
main.cpp
:
#include <iostream>
#include <numeric>
#include <string>
#include "vapply.h"
std::string test1(uint64_t a)
{
return std::to_string(a);
}
int main(int argc, char **argv)
{
std::vector <uint64_t> v(17);
std::iota(v.begin(), v.end(), 0);
vapply(test1, v);
return 0;
}
Upvotes: 2
Views: 198
Reputation: 711
auto q = std::async(std::launch::async, &vapply::docall, std::forward <Fn>(fn), v, i, i + funcToPerform);
this line does not compile because to pass a method as an argument to a function it have to be bound to an instance, this
in this case. so use std::bind
or better a lambda like this: auto fut = std::async(std::launch::async, [&] { return docall(std::forward <Fn>(fn), v, i, i + funcToPerform); });
to get the result of a future
use its get()
method like this auto q = fut.get();
but keep in mind future::get()
is a blocking call so calling async()
and then future::get()
in a loop will not run more than one thread. instead save the futures in one loop and call get() for each future in other loop.
Upvotes: 1