0

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!

jakerz
  • 123
  • 5
  • 1
    Have you tried to call `Pool.submit(Foo, std::ref(arg1), std::ref(arg2), size);`? – Daniel Langr Apr 22 '21 at 06:27
  • 1
    Almost duplicated question [Why does passing object reference arguments to thread function fails to compile?](https://stackoverflow.com/questions/8299545/why-does-passing-object-reference-arguments-to-thread-function-fails-to-compile). – 273K Apr 22 '21 at 06:29
  • 1
    Is jumping through all those hoops really necessary? `std::packaged_task` is all that's needed to put a task in a queue. – rustyx Apr 22 '21 at 08:08
  • @DanielLangr This fixed the issue! If you submit this as an answer, I can vote for it! – jakerz Apr 22 '21 at 20:45

1 Answers1

1

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

Daniel Langr
  • 22,196
  • 3
  • 50
  • 93