这是从
我的回答到另一个非常相似的帖子进行改编的。
让我们来构建一个
ThreadPool
类:
class ThreadPool {
public:
void Start();
void QueueJob(const std::function<void()>& job);
void Stop();
bool busy();
private:
void ThreadLoop();
bool should_terminate = false;
std::mutex queue_mutex;
std::condition_variable mutex_condition;
std::vector<std::thread> threads;
std::queue<std::function<void()>> jobs;
};
ThreadPool::Start
为了实现一个高效的线程池,一旦根据num_threads创建了线程,最好不要创建新的线程或销毁旧的线程(通过join)。这样会有性能损失,甚至可能使您的应用程序比串行版本更慢。因此,我们保持一个线程池,可以随时使用(如果它们还没有运行作业)。
每个线程应该运行自己的无限循环,不断等待新任务并执行。
void ThreadPool::Start() {
const uint32_t num_threads = std::thread::hardware_concurrency();
for (uint32_t ii = 0; ii < num_threads; ++ii) {
threads.emplace_back(std::thread(&ThreadPool::ThreadLoop,this))
}
}
ThreadPool::ThreadLoop
无限循环函数。这是一个while (true)
循环,等待任务队列打开。
void ThreadPool::ThreadLoop() {
while (true) {
std::function<void()> job;
{
std::unique_lock<std::mutex> lock(queue_mutex);
mutex_condition.wait(lock, [this] {
return !jobs.empty() || should_terminate;
});
if (should_terminate) {
return;
}
job = jobs.front();
jobs.pop();
}
job();
}
}
3. ThreadPool::QueueJob
将一个新的任务添加到线程池中;使用锁来避免数据竞争。
void ThreadPool::QueueJob(const std::function<void()>& job) {
{
std::unique_lock<std::mutex> lock(queue_mutex);
jobs.push(job);
}
mutex_condition.notify_one();
}
使用它的方法:
thread_pool->QueueJob([] { });
4.
ThreadPool::busy
线程池的忙碌状态
bool ThreadPool::busy() {
bool poolbusy;
{
std::unique_lock<std::mutex> lock(queue_mutex);
poolbusy = !jobs.empty();
}
return poolbusy;
}
busy()函数可以在while循环中使用,以便主线程在调用线程池析构函数之前等待线程池完成所有任务。
ThreadPool::Stop
停止线程池。
void ThreadPool::Stop() {
{
std::unique_lock<std::mutex> lock(queue_mutex);
should_terminate = true;
}
mutex_condition.notify_all();
for (std::thread& active_thread : threads) {
active_thread.join();
}
threads.clear();
}
一旦你整合了这些要素,你就拥有了自己的动态线程池。这些线程始终运行着,等待工作的到来。
如果有语法错误,我很抱歉,因为我自己输入这段代码,我的记忆力不好。很抱歉我不能提供完整的线程池代码,那将违背我的职业操守。
备注:
- 匿名代码块用于在退出时,使其内部创建的std::unique_lock变量超出作用域,从而释放互斥锁。
- ThreadPool::Stop并不会终止当前正在运行的任务,它只是通过active_thread.join()等待它们完成。