This implementation of thread pool that includes support for suspend functionality, which is necessary for refreshing reference data periodically.
I would appreciate your review to ensure that there are no bugs and that the implementation is well-structured. Although everything seems to be working fine, I am still a novice in this area and might have overlooked some details that could be improved.
class ThreadPool
{
public:
ThreadPool(size_t size = std::thread::hardware_concurrency())
{
threads_.reserve(size);
for (size_t i = 0; i < size; ++i)
{
threads_.emplace_back([this, tid = i]()
{ thread_func(tid); });
}
}
template <typename F, typename... Args>
void push(F &&f, Args &&...args)
{
{
std::unique_lock<std::mutex> lock(mutex_);
tasks_.emplace([=]() { f(args...); });
}
condition_.notify_one();
}
~ThreadPool()
{
{
std::lock_guard<std::mutex> lock(mutex_);
stop_ = true;
}
condition_.notify_all();
for (auto &thread : threads_)
{
thread.join();
}
}
void suspend()
{
suspend_mutex_.lock();
{
std::lock_guard<std::mutex> lock(mutex_);
suspender_id_ = std::this_thread::get_id();
suspend_ = true;
}
}
void resume()
{
std::lock_guard<std::mutex> lock(mutex_);
if (suspender_id_ != std::this_thread::get_id()) { return; }
suspend_ = false;
suspend_mutex_.unlock();
}
bool isSuspended()
{
std::unique_lock<std::mutex> lock(mutex_);
return active_threads_ == 0;
}
private:
void thread_func(size_t thread_id)
{
{
std::unique_lock<std::mutex> lock(mutex_);
std::cout << "starting: " << thread_id << '\n';
}
while (true)
{
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(mutex_);
condition_.wait(lock, [this]
{ return !tasks_.empty() || stop_; });
if (stop_ && tasks_.empty())
{
--active_threads_;
return;
}
if (suspend_)
{
continue;
}
task = std::move(tasks_.front());
tasks_.pop();
++active_threads_;
}
if (task) task();
std::unique_lock<std::mutex> lock(mutex_);
--active_threads_;
}
}
std::vector<std::thread> threads_;
std::queue<std::function<void()>> tasks_;
std::mutex mutex_;
std::mutex suspend_mutex_;
std::condition_variable condition_;
std::thread::id suspender_id_;
bool stop_ {false};
bool suspend_ {false};
std::size_t active_threads_ {0};
};
Usage example
void thread_func(ThreadPool& pool)
{
int count = 0;
// Push some tasks to the thread pool
pool.push([&count]() { count += 1; });
pool.push([&count]() { count += 2; });
pool.push([&count]() { count += 3; });
// Suspend the thread pool and wait for a short period of time
pool.suspend();
std::cout << "Thread pool suspended\n";
std::this_thread::sleep_for(std::chrono::seconds(1));
// Push some more tasks and verify that they're not executed
pool.push([&count]() { count += 4; });
pool.push([&count]() { count += 5; });
pool.push([&count]() { count += 6; });
std::cout << "More tasks pushed, count = " << count << "\n";
// Resume the thread pool and wait for another short period of time
pool.resume();
std::cout << "Thread pool resumed\n";
std::this_thread::sleep_for(std::chrono::seconds(1));
// Verify that all tasks have executed
std::cout << "Final count = " << count << "\n";
if (count == 21) {
std::cout << "Test passed\n";
} else {
std::cout << "Test failed\n";
}
}
int main()
{
ThreadPool pool(8);
while(1)
for (int i = 0 ; i < 2 ; i++)
{
std::jthread t(thread_func, std::ref(pool)); // Create a new thread and pass in the thread pool by reference
}
}