awhile ago I asked about a thread pool implementation I had made. I took the advice down and modified it a lot over time to fit my needs. It has been serving me extremely well in all of my projects but I was wondering if there was anything else I could improve on?
Here is the class:
class ThreadPool{
public:
/* Constructor */
ThreadPool(uint8_t numThreads) {
assert(numThreads > 0);
createThreads(numThreads);
}
//Destructor
~ThreadPool(){
shutdown = true;
notifier.notify_all();
for(int i = 0; i < threads.size(); ++i){
threads[i].join();
}
}
//add any arg # function to queue
template <typename Func, typename... Args >
auto push(Func&& f, Args&&... args){
//get return type of the function
typedef decltype(f(args...)) retType;
std::packaged_task<retType()> task(std::bind(f, args...));
std::future<retType> future = task.get_future();
{
// lock jobQueue mutex, add job to the job queue
std::unique_lock<std::mutex> lock(JobMutex);
jobQueue.emplace(std::packaged_task<void()>(std::move(task)));
}
notifier.notify_one();
return future;
}
private:
std::vector<std::thread> threads;
std::queue<std::packaged_task<void()>> jobQueue;
std::condition_variable notifier;
std::mutex JobMutex;
std::atomic<bool> shutdown = false;
void createThreads(uint8_t numThreads) {
auto threadFunc = [this]() {
while (true) {
std::packaged_task<void()> job;
{
std::unique_lock<std::mutex> lock(JobMutex);
notifier.wait(lock, [this] {return !jobQueue.empty(); });
if(shutdown){
break;
}
//strange bug where it will continue even if the job queue is empty
if (jobQueue.size() < 1)
continue;
job = std::move(jobQueue.front());
jobQueue.pop();
}
job();
}
};
threads.reserve(numThreads);
for (int i = 0; i != numThreads; ++i) {
threads.emplace_back(std::thread(threadFunc));
}
}
ThreadPool(const ThreadPool& other) = delete;
void operator=(const ThreadPool& other) = delete;
}; /* end ThreadPool Class */