线程池流程图:
(一)首先确定好咱们的线程池类,并写好基本的接口
class ThreadPool
{
public:
/// <summary>
/// 初始化线程池
/// </summary>
/// <param name="num">线程数量</param>
void Init(int num);
/// <summary>
/// 启动所有线程
/// </summary>
void Start();
/// <summary>
/// 线程池退出
/// </summary>
void Stop();
bool is_exit() { return is_exit_; }
private:
//线程池线程入口函数
void Run();
int thread_num_ = 0;//线程数量
std::mutex mux_;
bool is_exit_ = false; //线程池退出
};
1、初始化线程池数量(线程安全)
void ThreadPool::Init(int num)
{
unique_lock<mutex> lock(mux_);
this->thread_num_ = num;
}
2、启动线程池里的线程(线程安全)
void ThreadPool::Start()
{
unique_lock<mutex> lock(mux_);
if (thread_num_ <= 0)
{
cerr << "Please Init ThreadPool" << endl;
return;
}
if (!threads_.empty())//判断线程池是否是空
{
cerr << " Thread pool has start!" << endl;
return;
}
cout << "Pool Start " << endl;
for (int i = 0; i < thread_num_; i++)
{
auto th = make_shared<thread>(&ThreadPool::Run, this);
threads_.push_back(th);//复制,shared_ptr引用计数加一
}//出栈区引用计数减一
}
//线程入口函数
void ThreadPool::Run()
{
cout << "begin thread pool " << this_thread::get_id() << endl;
cout << "end thread pool " << this_thread::get_id() << endl;
}
这里通过智能指针shared_ptr来管理线程,通过智能指针来自动管理线程的生命周期
3、线程池退出
void ThreadPool::Stop()
{
is_exit_ = true;
threads_.clear();//清理线程池
}
(二)确定任务接口
class Task
{
public:
virtual int Run() = 0;
};
class Mytask : public Task
{
public:
int Run()
{
cout << " =============================== " << endl;
cout << this_thread::get_id() << "MyTask" << name << endl;
for (int i = 0; i < 10; i++)
{
if (is_exit())break;
cout << " . "<< flush ;
this_thread::sleep_for(1s);
}
return 0;
}
};
1、给线程池加入用来加入任务的接口AddTask()和用于捕获任务的接口 GetTask()
class ThreadPool
{
public:
/// <summary>
/// 初始化线程池
/// </summary>
/// <param name="num">线程数量</param>
void Init(int num);
/// <summary>
/// 启动所有线程,必须先调用Init
/// </summary>
void Start();
/// <summary>
/// 线程池退出
/// </summary>
void Stop();
//线程池退出标志位
bool is_exit() { return is_exit_; }
//通过shared_ptr来管理任务链表,出栈自动释放
void AddTask( std::shared_ptr<Task> task );
std::shared_ptr<Task> GetTask();
private:
//线程池线程入口函数
void Run();
int thread_num_ = 0;//线程数量
std::mutex mux_;
//线程池容器
std::vector< std::shared_ptr<std::thread> > threads_;
//任务链表
std::list< std::shared_ptr<Task> > tasks_;
std::condition_variable cv_;//条件变量
bool is_exit_ = false; //线程池退出
};
2、向线程池里加入任务(线程安全)
void ThreadPool::AddTask(std::shared_ptr<Task> task)
{
unique_lock<mutex> lock(mux_);
tasks_.push_back(task);//把任务加入任务链表
lock.unlock();
cv_.notify_one();
}
3、线程池捕获任务(线程安全)
std::shared_ptr<Task> ThreadPool::GetTask()
{
unique_lock<mutex> lock(mux_);//线程安全
//任务链表为空,阻塞等待通知
if (tasks_.empty())
cv_.wait(lock);
//收到通知以后链表还是空说明是通知退出线程池
if (tasks_.empty())
return nullptr;
//将任务取出返回出去
auto task = tasks_.front();
//任务弹出队列
tasks_.pop_front();
return task;
}
4、重写线程入口函数
void ThreadPool::Run()
{
cout << "begin thread pool " << this_thread::get_id() << endl;
while (!is_exit())
{
auto task = GetTask();
if (!task)continue;
try {
task->Run();
}
catch (...)//表示捕获任何类型的异常
{
}
}
cout << "end thread pool " << this_thread::get_id() << endl;
}
5、任务处理流程思路分析
当启动线程进入线程入口函数以后会再调用任务捕获函数,由于没有收到信号通知,线程会阻塞等待条件变量通知 cv_.wait(lock);
当我们在主函数里向线程池添加任务时 pool.AddTask(task3); ,线程会收到条件变量通知,即通过cv_.notify_one来按顺序一个个通知在排队的线程,
(注意!在cv_.notify_one之前要先解锁unlock,因为wait收到信号以后会再次锁住资源,如果这里没释放锁,则wait那边就会一直等待锁资源,照成死锁!!)
此时任务捕获函数中就会接收到信号,然后结束阻塞
(三)取得任务返回值,线程回到线程池
1、重写任务对象和线程入口函数
class Task
{
public:
virtual int Run() = 0;
std::function<bool()> is_exit = nullptr;
auto GetReturn()
{
//阻塞等待set_value
return proms_.get_future().get();
}
void SetValue(int v)
{
proms_.set_value(v);
}
private:
//用来接收返回值
std::promise<int> proms_;
};
线程入口函数 :
while (!is_exit())
{
auto task = GetTask();
if (!task)continue;
try {
auto re = task->Run();
task->SetValue(re);
}
catch (...)//表示捕获任何类型的异常
{
}
}
2、思路分析
如果想在线程池外拿到任务处理的结果就需要通过promise—future来异步获取结果
当在外面调用GetReturn()时,外面的主线程就会阻塞在这里promise.get_future().get();,等待promise.set_value()设置值,当SetValue()在线程入口函数Run里被调用时,外面主线程就会收到任务返回值,取消阻塞
(四)退出线程池
1、重写Stop函数
void ThreadPool::Stop()
{
is_exit_ = true;
cv_.notify_all();
for (auto& th : threads_)
{
th->join();
}
unique_lock<mutex> lock(mux_);
threads_.clear();
}
设置is_exit_为true以后GetTask()会返回nullptr,防止还有线程阻塞在前面的wait,通过条件变量
cv_.notify_all()给所有线程发通知,取消阻塞,然后退出给task返回nullptr