ruser45381
ruser45381

Reputation: 205

Using std::async with function requiring function and argument parameters

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

Answers (1)

Slava Zhuyko
Slava Zhuyko

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

Related Questions