0

My question is from this implementation of a ThreadPool class in C++11. Following is relevant parts from the code:

  1. whenever enqueue is called on the threadPool object, it binds the passed function with all passed arguments, to create a shared_ptr of std::packaged_task:
    auto task = std::make_shared< std::packaged_task<return_type()> >(
        std::bind(std::forward<F>(f), std::forward<Args>(args)...)
    );
  1. extracts the future from this std::packaged_taskto return to the caller and stores this task in a std::queue<std::function<void()>> tasks;.

  2. In the constructor, it waits for the task in queue, and if it finds one, it executes the task:

    for(size_t i = 0;i<threads;++i)
        workers.emplace_back(
            [this]
            {
                for(;;)
                {
                    std::function<void()> task;
                    {
                        std::unique_lock<std::mutex> lock(this->queue_mutex);
                        this->condition.wait(lock,[this]{ return !this->tasks.empty(); });
                        task = std::move(this->tasks.front());
                        this->tasks.pop();
                    }
                    task();
                }
            }
        );

Now, based on this, following is my questions:

  1. If std::packaged_task was stored in a std::queue<std::function<void()>>, then it just becomes a std::function object, right? then how does it still write to the shared state of std::future extracted earlier?

  2. If stored std::packaged_task was not just a std::function object but still a std::packaged_taskthen when a std::thread executes task() through a lambda (code inside constructor), then why doesn't it run on another thread? as std::packaged_task are supposed to run on another thread, right?

As my questions suggest, I am unable to understand the conversion of std::packaged_task into std::function and the capability of std::function to write to the shared state of std::future. Whenever I tested this code with n threads, the maximum number of thread ids I could get was n but never more than n. Here is the complete code (including that of ThreadPool and it also includes a main function which counts the number of threads created).

aniliitb10
  • 1,259
  • 10
  • 14
  • 1
    1. It doesn't *become* `std::function` - it gets wrapped in a `std::function` object. `std::function` can wrap any callable (an object to which a function call operator can be applied). When `std::function` is called, it applies that operator to the object it wraps. – Igor Tandetnik Dec 07 '19 at 14:31
  • 2. I don't understand this question. Why doesn't *what* run on another thread? What makes you believe it doesn't? What problem do you believe exists in the code? What outcome do you expect, and what do you observe instead? – Igor Tandetnik Dec 07 '19 at 14:32
  • The worker thread is calling tasks, so I expcted each `task` to run on another thread as each `task` is actually a `std::packaged_task`. But then, let's say there were 100 such tasks, then I expect that the func, which each task is wrapping, should be called by 100 different threads, having 100 different thread ids. But it doesn't happen. The number of thread ids are never greater than the size of threadpool. That implies that these packaged tasks are getting called on the worker thread itself which I can't understand why/how? – aniliitb10 Dec 07 '19 at 16:16
  • It made a lot sense when you described `std::function` like that. Whenever I read about `std::function`, I always felt like every callable just decays into `std::function`, just like an array decays into a pointer and looses its original identity. thank you! – aniliitb10 Dec 07 '19 at 16:22
  • `std::packaged_task` doesn't by itself create any threads. The actual function it wraps is executed on whichever thread calls `std::packaged_task::operator()`. – Igor Tandetnik Dec 07 '19 at 19:18
  • oh, this makes a lot of sense. thanks! – aniliitb10 Dec 13 '19 at 06:23

0 Answers0