jakerz
jakerz

Reputation: 123

Thread pool in C++ not using references from args

I am trying to get a thread pool to run a method were the arguments are passed by reference. The method I want to run is:

void Foo(const std::vector<std::string> &arg1, std::vector<int> &arg2, int size) {//modify elements of arg2}

I am submitting this function to a thread pool:

Pool.submit(Foo, arg1,arg2,size);
...

template<typename F, typename... Args>
void submit(F&& f, Args&&... args) {
        //Create a callable std::function with parameters that will be executed by a free std::thread

        std::function<decltype(f(args...))()> func = std::bind(std::forward<F>(f), std::forward<Args>(args)...);

        //Place the callable std::function into a shared_ptr

        auto pTask = std::make_shared<std::packaged_task<decltype(f(args...))()>> (func);

        //Wrap packaged task into void std::function using a lambda std::function

        std::function<void()> pWrappedTask = [pTask]() {(*pTask)();};

        //Increment this for job status tracking
        JobsRequested++;
        //Enqueue generic wrapper std::function
        JobQ.enqueue(pWrappedTask);
        //Wake up one std::thread if its waiting
        PoolCondVar.notify_one();
    }

I found that the the arguments are not being passed by reference, so when the thread calls Foo, the original variable does not get modified. If I call the function without using the thread pool, the Foo works correctly. I think it has something to do with how I am setting up the function std::function<decltype(f(args...))()> func = std::bind(std::forward<F>(f), std::forward<Args>(args)...); because when I debug the function within each thread, the address of my arguments change between threads. Thank you for any suggestions!

Upvotes: 0

Views: 427

Answers (1)

Daniel Langr
Daniel Langr

Reputation: 23527

According to std::bind documentation:

The return type of std::bind holds a member object of type std::decay<F>::type constructed from std::forward<F>(f), and one object per each of args..., of type std::decay<Arg_i>::type, similarly constructed from std::forward<Arg_i>(arg_i).

Thanks to decay, the arguments are "stored-by-value", which means the arguments are either copy-constructed or move-constructed. This explains their different address.

As usually in such situations, the solution is in using std::reference_wrapper:

Pool.submit(Foo, std::ref(arg1), std::ref(arg2), size);

Live demo: https://godbolt.org/z/4M3vK1cr9

Upvotes: 1

Related Questions