C++简单线程池
1.线程池
1.1 线程池定义
- 简单来说就是有一堆已经创建好的线程(最大数目一定),初始化时他们都处于空闲状态,当有新的任务进来时,从线程池中取出一个空闲的线程处理任务,然后当任务处理完成之后,该线程被重新放回到线程池中,供其他的任务使用,当线程池中的线程都在处理任务时,就没有空闲线程供使用,此时,若有新的任务产生,只能等待线程池中有线程结束任务空闲才能执行。
- 线程池: 当进行并行的任务作业操作时,线程的建立与销毁的开销是,阻碍性能进步的关键,因此线程池,由此产生。使用多个线程,无限制循环等待队列,进行计算和操作。帮助快速降低和减少性能损耗。
1.2 为何使用线程池
优点:
-
性能:每开启一个新的线程都要消耗内存空间及资源(默认情况下大约1 MB的内存),同时多线程情况下操作系统必须调度可运行的线程并执行上下文切换,所以太多的线程还对性能不利。而线程池其目的是为了减少开启新线程消耗的资源(使用线程池中的空闲线程,不必再开启新线程,以及统一管理线程(线程池中的线程执行完毕后,回归到线程池内,等待新任务))。
-
时间:无论何时启动一个线程,都需要时间(几百毫秒),用于创建新的局部变量堆,线程池预先创建了一组可回收线程,因此可以缩短过载时间。
缺点:
线程池缺点:线程池的性能损耗优于线程(通过共享和回收线程的方式实现),但是:
- 线程池不支持线程的取消、完成、失败、通知等交互性操作。
- 线程池不支持线程执行的先后次序排序。
- 不能设置池化线程(线程池内的线程)的Name,会增加代码调试难度。
- 线程池通常都是后台线程,优先级为ThreadPriority.Normal。
- 线程池阻塞会影响性能(阻塞会使CLR错误地认为它占用了大量CPU,CLR能够检测或补偿(往池中注入更多线程),但是这可能使线程池收到后续超负荷的影响。Task解决了这个问题)。
- 线程池使用的是全局队列,全局队列中的线程依旧会存在竞争共享资源的情况,从而影响性能(Task使用本地队列解决了这个问题)
1.3 线程池的组成
线程池管理器:
- 任务: 创先一定数量的线程,启动线程,调配认为,管理着线程池。
- 本篇线程池目前只需要启动(start()),停止方法(stop()),及任务添加方法(addTask).
start()创建一定数量的线程池,进行线程循环.
stop()停止所有线程循环,回收所有资源.
addTask()添加任务.
工作线程:
- 线程池中线程,在线程池中等待并执行分配的任务。
- 本篇选用条件变量实现等待和通知机制。
任务接口:
- 添加任务的接口,以供工作线程调度任务的执行。
任务队列:
- 用于存放没有处理的任务。提供一种缓冲机制
同时任务队列具有调度功能,高优先级的任务放在任务队列前面;本篇选用priority_queue 与pair的结合用作任务优先队列的结构.
1.4 线程池工作的四种情况
假设我们的线程池大小为3,任务队列目前不做大小限制.
- 1、主程序当前没有任务要执行,线程池中的任务队列为空闲状态.
此情况下所有工作线程处于空闲的等待状态,任务缓冲队列为空. - 2、主程序添加小于等于线程池中线程数量的任务.
此情况基于情形1,所有工作线程已处在等待状态,主线程开始添加三个任务,添加后通知(notif())唤醒线程池中的线程开始取(take())任务执行. 此时的任务缓冲队列还是空。 - 3、主程序添加任务数量大于当前线程池中线程数量的任务.
此情况发生情形2后面,所有工作线程都在工作中,主线程开始添加第四个任务,添加后发现现在线程池中的线程用完了,于是存入任务缓冲队列。工作线程空闲后主动从任务队列取任务执行. - 4、主程序添加任务数量大于当前线程池中线程数量的任务,且任务缓冲队列已满.
此情况发生情形3且设置了任务缓冲队列大小后面,主程序添加第N个任务,添加后发现池子中的线程用完了,任务缓冲队列也满了,于是进入等待状态、等待任务缓冲队列中的任务腾空通知。
2.carto中线程池+任务
2.1 Task 任务队列
2.1.1 头文件
#ifndef CARTOGRAPHER_COMMON_TASK_H_
#define CARTOGRAPHER_COMMON_TASK_H_
#include <set>
#include <mutex>
#include "glog/logging.h"
#include "thread_pool.h"
namespace cartographer {
namespace common {
class ThreadPoolInterface;
class Task {
public:
friend class ThreadPoolInterface;
//工作项目回调
using WorkItem = std::function<void()>;
//新增,已分派,依赖已完成,正在运行,已完成
enum State { NEW, DISPATCHED, DEPENDENCIES_COMPLETED, RUNNING, COMPLETED };
Task() = default;
~Task();
//得到该 任务 的状态
State GetState();
// State must be 'NEW'.
void SetWorkItem(const WorkItem& work_item);
// State must be 'NEW'. 'dependency' may be nullptr, in which case it is
// assumed completed. 依赖可以为空,但任务必须是 new
void AddDependency(std::weak_ptr<Task> dependency);
private:
// Allowed in all states. 在所有状态下都允许
void AddDependentTask(Task* dependent_task);
// State must be 'DEPENDENCIES_COMPLETED' and becomes 'COMPLETED'.
// 状态必须为“DEPENDENCIES_COMPLETED”并变为“COMPLETED”
void Execute();
// State must be 'NEW' and becomes 'DISPATCHED' or 'DEPENDENCIES_COMPLETED'.
// 状态必须为“NEW”并变为“DISPATCHED”或“DEPENDENCIES_COMPLETED”。
void SetThreadPool(ThreadPoolInterface* thread_pool) ;
// State must be 'NEW' or 'DISPATCHED'. If 'DISPATCHED', may become
// 'DEPENDENCIES_COMPLETED'.
void OnDependenyCompleted();
WorkItem work_item_ ;
ThreadPoolInterface* thread_pool_to_notify_ = nullptr;
State state_ = NEW;
unsigned int uncompleted_dependencies_ = 0;
std::set<Task*> dependent_tasks_ ;
std::mutex mutex_;
};
} // namespace common
} // namespace cartographer
#endif // CARTOGRAPHER_COMMON_TASK_H_
2.1.2 .cc
/*
* Copyright 2018 The Cartographer Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "task.h"
namespace cartographer {
namespace common {
Task::~Task() {
// TODO(gaschler): Relax some checks after testing.
if (state_ != NEW && state_ != COMPLETED) {
LOG(WARNING) << "Delete Task between dispatch and completion.";
}
}
Task::State Task::GetState() {
std::unique_lock<std::mutex> locker(mutex_);
return state_;
}
void Task::SetWorkItem(const WorkItem& work_item) {
std::unique_lock<std::mutex> lock(mutex_);
CHECK_EQ(state_, NEW);
work_item_ = work_item;
}
// 添加任务依赖,将其放入 dependent_tasks_
void Task::AddDependency(std::weak_ptr<Task> dependency) {
std::shared_ptr<Task> shared_dependency;
{
std::unique_lock<std::mutex> lock(mutex_);
CHECK_EQ(state_, NEW);
if ((shared_dependency = dependency.lock())) {
++uncompleted_dependencies_;
}
}
if (shared_dependency) {
shared_dependency->AddDependentTask(this);
}
}
void Task::SetThreadPool(ThreadPoolInterface* thread_pool) {
std::unique_lock<std::mutex> lock(mutex_);
CHECK_EQ(state_, NEW);
state_ = DISPATCHED;
thread_pool_to_notify_ = thread_pool;
if (uncompleted_dependencies_ == 0) {
state_ = DEPENDENCIES_COMPLETED;
CHECK(thread_pool_to_notify_);
thread_pool_to_notify_->NotifyDependenciesCompleted(this);
}
}
void Task::AddDependentTask(Task* dependent_task) {
std::unique_lock<std::mutex> lock(mutex_);
if (state_ == COMPLETED) {
dependent_task->OnDependenyCompleted();
return;
}
bool inserted = dependent_tasks_.insert(dependent_task).second;
CHECK(inserted) << "Given dependency is already a dependency.";
}
void Task::OnDependenyCompleted() {
std::unique_lock<std::mutex> lock(mutex_);
CHECK(state_ == NEW || state_ == DISPATCHED);
--uncompleted_dependencies_;
if (uncompleted_dependencies_ == 0 && state_ == DISPATCHED) {
state_ = DEPENDENCIES_COMPLETED;
CHECK(thread_pool_to_notify_);
thread_pool_to_notify_->NotifyDependenciesCompleted(this);
}
}
void Task::Execute() {
{
std::unique_lock<std::mutex> lock(mutex_);
CHECK_EQ(state_, DEPENDENCIES_COMPLETED);
state_ = RUNNING;
}
// Execute the work item.
if (work_item_) {
work_item_();
}
std::unique_lock<std::mutex> lock(mutex_);
state_ = COMPLETED;
for (Task* dependent_task : dependent_tasks_) {
dependent_task->OnDependenyCompleted();
}
}
} // namespace common
} // namespace cartographer
2.2 线程池
2.2.1 头文件
#ifndef CARTOGRAPHER_COMMON_THREAD_POOL_H_
#define CARTOGRAPHER_COMMON_THREAD_POOL_H_
#include <deque>
#include <functional>
#include <memory>
#include <thread>
#include <vector>
#include <map>
#include <mutex>
#include <condition_variable>
#include "task.h"
namespace cartographer {
namespace common {
class Task;
class ThreadPoolInterface {
public:
ThreadPoolInterface() {}
virtual ~ThreadPoolInterface() {}
virtual std::weak_ptr<Task> Schedule(std::unique_ptr<Task> task) = 0; //进程表
protected:
void Execute(Task* task);
void SetThreadPool(Task* task);
private:
friend class Task;
virtual void NotifyDependenciesCompleted(Task* task) = 0;
};
class ThreadPool : public ThreadPoolInterface {
public:
explicit ThreadPool(int num_threads);
~ThreadPool();
ThreadPool(const ThreadPool&) = delete;
ThreadPool& operator=(const ThreadPool&) = delete;
// When the returned weak pointer is expired, 'task' has certainly completed,
// so dependants no longer need to add it as a dependency.
std::weak_ptr<Task> Schedule(std::unique_ptr<Task> task) override;
private:
void DoWork();
void NotifyDependenciesCompleted(Task* task) override;
std::mutex mutex_; //同步
std::condition_variable cv_task;
bool running_ = true;
std::vector<std::thread> pool_ ; // 线程池
std::deque<std::shared_ptr<Task>> task_queue_ ; //任务队列
std::map <Task*, std::shared_ptr<Task>> tasks_not_ready_; //
};
} // namespace common
} // namespace cartographer
#endif // CARTOGRAPHER_COMMON_THREAD_POOL_H_
2.2.2 .cc
/*
* Copyright 2016 The Cartographer Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "thread_pool.h"
#ifndef WIN32
#include <unistd.h>
#endif
#include <algorithm>
#include <chrono>
#include <numeric>
#include "task.h"
#include "glog/logging.h"
namespace cartographer {
namespace common {
void ThreadPoolInterface::Execute(Task* task) { task->Execute(); }
void ThreadPoolInterface::SetThreadPool(Task* task) {
task->SetThreadPool(this);
}
//构造 线程池 参数是多少个线程
ThreadPool::ThreadPool(int num_threads) {
//线程池个数小于1时,设定一个线程
int threadNum = num_threads <1 ?1:num_threads;
std::unique_lock<std::mutex> lock(mutex_);
for (int i = 0; i != threadNum; ++i) {
//构造每个线程池中的线程
pool_.emplace_back([this]() { ThreadPool::DoWork(); });
}
}
ThreadPool::~ThreadPool() {
{
std::unique_lock<std::mutex> lock(mutex_);
CHECK(running_);
running_ = false;
}
cv_task.notify_all(); // 唤醒所有线程执行
for (std::thread& thread : pool_) {
thread.join();
}
}
void ThreadPool::NotifyDependenciesCompleted(Task* task) {
std::unique_lock<std::mutex> lock(mutex_);
auto it = tasks_not_ready_.find(task);
CHECK(it != tasks_not_ready_.end());
task_queue_.push_back(it->second);
tasks_not_ready_.erase(it);
}
std::weak_ptr<Task> ThreadPool::Schedule(std::unique_ptr<Task> task) {
std::shared_ptr<Task> shared_task;
{
std::unique_lock<std::mutex> lock(mutex_);
auto insert_result =
tasks_not_ready_.insert(std::make_pair(task.get(), std::move(task)));
CHECK(insert_result.second) << "Schedule called twice";
shared_task = insert_result.first->second;
}
SetThreadPool(shared_task.get());
return shared_task;
}
void ThreadPool::DoWork() {
#ifdef __linux__
// This changes the per-thread nice level of the current thread on Linux. We
// do this so that the background work done by the thread pool is not taking
// away CPU resources from more important foreground threads.
CHECK_NE(nice(10), -1);
#endif
//一个while 循环
for (;;) {
//一个共享任务指针
std::shared_ptr<Task> task;
{
//数据互斥锁
std::unique_lock<std::mutex> lock(mutex_);
//如果 任务队列是空 或者 线程正在运行时
cv_task.wait(lock, [this](){return !task_queue_.empty() || !running_;});
//当任务不为空时,将该任务赋给上面任务指针,并弹出
if (!task_queue_.empty()) {
task = std::move(task_queue_.front());
task_queue_.pop_front();
} else if (!running_) {
return;
}
}
CHECK(task);
CHECK_EQ(task->GetState(), common::Task::DEPENDENCIES_COMPLETED);
//执行任务
Execute(task.get());
}
}
} // namespace common
} // namespace cartographer
2.3 测试
namespace CartoCom = cartographer::common;
int test_int = 0;
void Test(int& index,const std::string& test){
const int add_index = 5;
int print_index = add_index+index;
LOG(ERROR)<<"print: "<<test;
while(index<print_index){
index +=1;
sleep(1);
LOG(ERROR)<<"index=: "<<index;
}
}
LOG(ERROR)<<"start pointcloud_deal_node!";
CartoCom::ThreadPool thread (3);
std::unique_ptr<CartoCom::Task> test_task = CartoCom::make_unique<CartoCom::Task>();
test_task->SetWorkItem([=](){Test(test_int,"111111111");});
std::weak_ptr<CartoCom::Task> return_ptr = thread.Schedule(std::move(test_task));
std::unique_ptr<CartoCom::Task> test_task1 = CartoCom::make_unique<CartoCom::Task>();
test_task1->SetWorkItem([=](){Test(test_int,"22222222");});
test_task1->AddDependency(return_ptr);
std::weak_ptr<CartoCom::Task> return_ptr1 = thread.Schedule(std::move(test_task1));
std::unique_ptr<CartoCom::Task> test_task2 = CartoCom::make_unique<CartoCom::Task>();
test_task2->SetWorkItem([=](){Test(test_int,"33333333");});
test_task2->AddDependency(return_ptr1);
std::weak_ptr<CartoCom::Task> return_ptr2 = thread.Schedule(std::move(test_task2));
LOG(ERROR)<<"end pointcloud_deal_node!";