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();
}
}
);
}
}
ThreadPool
s constructor is using theemplace_back
function ofstd::vector
to construct astd::thread
to the back of theworkers_m
variable.emplace_back
differs frompush_back
in 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::thread
s 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_m
is 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::move
s the task to do out of the queue,pop()
s it, and then releases its mutex which is held by aunique_lock
due to the lock's destructor. The thread then executes the task by calling it. This process is then repeated, due to thefor(;;)
loop