Can somebody explain where a thread is created on the lambda function below? What is the technique used? Can somebody recommend a reference to understand the semantics?
I posted the full code now:
class ThreadPool {
public:
ThreadPool(size_t);
template<class F, class... Args>
auto enqueue(F&& f, Args&&... args)
->std::future<typename std::result_of<F(Args...)>::type>;
~ThreadPool();
private:
// need to keep track of threads so we can join them
std::vector< std::thread > workers_m;
// the task queue
std::queue< std::function<void()> > tasks_m;
// synchronization
std::mutex queue_mutex_m;
std::condition_variable condition_m;
bool stop_m;
};
// the constructor just launches some amount of workers
inline ThreadPool::ThreadPool(size_t threads)
: stop_m(false)
{
std::thread::id id = std::this_thread::get_id();
for (size_t i = 0; i < threads; ++i)
{
workers_m.emplace_back(
[this]
{
for (;;)
{
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(this- >queue_mutex_m);
std::thread::id id1 = std::this_thread::get_id();
this->condition_m.wait(lock, [this]{ return this->stop_m || !this->tasks_m.empty(); });
std::thread::id id = std::this_thread::get_id();
if (this->stop_m && this->tasks_m.empty())
return;
task = std::move(this->tasks_m.front());
this->tasks_m.pop();
}
task();
}
}
);
}
}
ThreadPools constructor is using theemplace_backfunction ofstd::vectorto construct astd::threadto the back of theworkers_mvariable.emplace_backdiffers frompush_backin that it directly constructs the element type by forwarding the parameters passed to the constructor of the element type (std::thread).push_back, on the other hand, requires either an lvalue or a temporary element type and then will appropriately copy or move it into the back of the vector.ThreadPool's constructor will do this in a for loop to create the appropriate amount of worker threads, as specified by the constructor argument (threads). One ofstd::threads constructors takes a callable and any number of arguments and will run the function on the corresponding thread. In this case, the lambda being passed in is used to construct a thread that will run for as long as necessary (given by thefor(;;)), acquire a mutex to grant exclusive access to the task queue , and use a condition variable to wait until either a stop flag is set, or until there is something available in the queue.Once the condition variable is notified and one of the two conditions are true, the thread can continue. However, which of the two conditions were true is not known, hence the check for
which seems a bit flawed though, because if
stop_mis set the worker threads should stop processing tasks, even if the queue still contains tasks to do. But this check will only fire of both conditions are true.If the above check is false, the thread
std::moves the task to do out of the queue,pop()s it, and then releases its mutex which is held by aunique_lockdue to the lock's destructor. The thread then executes the task by calling it. This process is then repeated, due to thefor(;;)loop