当线程池出ThreadPool出作用域析构时,此时任务队列里如果还有任务,是等任务执行完再结束,还是不执行剩下的任务了?
这其实是两种设计,线程池都析构了,任务也就不需要了。但是,如果要设计一个通用的线程池,线程池析构是要等任务执行完成的,用户提交的任务可能比较耗时,任务还没执行完,线程池就出了作用域析构了。
之前的代码:
// 定义线程函数 线程池的所有线程从任务队列里面消费任务
void ThreadPool::threadFunc(int threadid)
{
auto lastTime = std::chrono::high_resolution_clock().now();
while (isPoolRunning_)
for(;;)
{
std::shared_ptr task;
{
// 先获取锁
std::unique_lock lock(taskQueMtx_);
// 下面测试用的
std::cout << "tid: " << std::this_thread::get_id() << "尝试获取任务..." << std::endl;
// cached模式下,有可能已经创建了很多线程,但是空闲线程如果超过了60s,就应该把多余的线 程
// 结束回收掉(超过initThreadSize_的线程要进行回收)
// 当前时间 - 上一次线程执行完的时间 > 60s
// 每秒钟返回一次 怎么区分超时返回,还是有任务待执行返回?
// 锁+双重判断
while (isPoolRunning_ && taskQue_.size() == 0)
{
if (poolMode_ == PoolMode::MODE_CACHED)
{
// 条件变量 超时返回了
if (std::cv_status::timeout == notEmpty_.wait_for(lock, std::chrono::seconds(1)))
{
// 当前时间
auto now = std::chrono::high_resolution_clock().now();
// 空闲时间
auto dur = std::chrono::duration_cast(now - lastTime);
if (dur.count() >= THREAD_MAX_IDLE_TIME && curThreadSize_ > initThreadSize_)
{
// 开始回收当前线程
// 记录线程数量的相关变量的值修改
// 把线程对象从线程列表删除
threads_.erase(threadid);
curThreadSize_--;
idleThreadSize_--;
std::cout << "threadid:" << std::this_thread::get_id() << " exit" << std::endl;
return;
}
}
}
else
{
// 等待任务队列不为空 notEmpty条件
notEmpty_.wait(lock);
}
// 1.线程本身在阻塞等待 线程池要结束 回收线程资源
//if (!isPoolRunning_)
//{
// // 线程回收
// threads_.erase(threadid);
// std::cout << "threadid:" << std::this_thread::get_id() << " exit" << std::endl;
// // 唤醒线程池
// exitCond_.notify_all();
// return;
//}
}
if (!isPoolRunning_)
{
break;
}
idleThreadSize_--;
// 下面测试用的
std::cout << "tid: " << std::this_thread::get_id() << "获取任务成功..." << std::endl;
// 从任务队列获取任务
task = taskQue_.front();
taskQue_.pop();
taskSize_--;
// 如果依然有剩余任务,继续通知其他线程来获取执行任务,提高了多线程同时并发获取任务、处理任务的能力
if (taskQue_.size() > 0)
{
notEmpty_.notify_all();
}
// 取出一个任务之后,进行通知,通知其他用户可以继续提交任务
notFull_.notify_all();
}// 一个线程取完任务之后,此刻就应该把锁释放掉,让多线程可以同时并发获取任务、处理任务
// 当前线程负责执行该任务
if (task != nullptr)
{
// task->run(); // 执行任务,把任务的返回值通过setVal方法给Result
task->exec();
}
idleThreadSize_++;
lastTime = std::chrono::high_resolution_clock().now(); // 更新线程执行完任务的时间
}
// 2.线程正在执行任务 线程池要结束 回收线程资源
threads_.erase(threadid);
std::cout << "threadid:" << std::this_thread::get_id() << " exit" << std::endl;
// 唤醒线程池
exitCond_.notify_all();
}
测试:
//线程池项目.cpp:此文件包含"main"函数。程序将在此处开始并结束。
//
#include
#include
#include
using namespace std;
#include"threadpool.hpp"
/*
有些场景,线程池是希望能够获取线程执行完任务以后的返回值的
举例:
计算 1 + ...+ 30000 的和
thread1 1 + ... + 10000
thread2 10000 + ... + 20000
thread3 20000 + ... + 30000
...
main thread: 给每一个线程分配计算的区间,并等待他们计算完返回结果,合并最终的结果即可。
*/
using ulong = unsigned long long;
class MyTask : public Task
{
public:
MyTask(int begin, int end)
:begin_(begin)
, end_(end)
{}
// 问题一:怎么设计run函数的返回值,可以表示任意类型。
Any run()
{
std::cout << "tid: " << std::this_thread::get_id() << "begin!" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(3));
ulong sum = 0;
for (int i = begin_; i <= end_; i++)
sum += i;
std::cout << "tid: " << std::this_thread::get_id() << "end!" << std::endl;
return sum;
}
private:
int begin_;
int end_;
};
int main()
{
{
ThreadPool pool;
pool.setMode(PoolMode::MODE_CACHED);
pool.start(2);
Result res1 = pool.submitTask(std::make_shared(1, 100000000));
Result res2 = pool.submitTask(std::make_shared(1, 100000000));
pool.submitTask(std::make_shared(1, 100000000));
pool.submitTask(std::make_shared(1, 100000000));
pool.submitTask(std::make_shared(1, 100000000));
// ulong sum1 = res1.get().cast_();
// cout << sum1 << endl;
}
cout << "main over!" << endl;
getchar();
#if 0
{
ThreadPool pool;
// 用户自己设置线程池的工作模式
pool.setMode(PoolMode::MODE_CACHED);
pool.start(4);
srand(time(0));
// 如何设计这里的Result机制呢?
size_t begin1 = clock();
Result res1 = pool.submitTask(std::make_shared(1, 100000000));
Result res2 = pool.submitTask(std::make_shared(100000001, 200000000));
Result res3 = pool.submitTask(std::make_shared(200000001, 300000000));
pool.submitTask(std::make_shared(200000001, 300000000));
pool.submitTask(std::make_shared(200000001, 300000000));
pool.submitTask(std::make_shared(200000001, 300000000));
ulong sum1 = res1.get().cast_();
ulong sum2 = res2.get().cast_();
ulong sum3 = res3.get().cast_();
size_t end1 = clock();
// Master - Slave线程模型
// Master线程用来分解任务,然后给各个Slaver线程分配任务
// 等待各个Slaver线程执行完成任务,返回结果
// Master线程合并各个任务结果,输出
// cout << "测试结果:" << (sum1 + sum2 + sum3) << "花费时间:" << end1 - begin1 << endl;
1+...300000000正确结果
//size_t begin2 = clock();
//ulong sum = 0;
//for (int i = 1; i <= 300000000; i++)
// sum += i;
//size_t end2 = clock();
//cout << "正确结果:" << sum << "花费时间:" << end2 - begin2 << endl;
}
/*pool.submitTask(std::make_shared());
pool.submitTask(std::make_shared());
pool.submitTask(std::make_shared());
pool.submitTask(std::make_shared());
pool.submitTask(std::make_shared());
pool.submitTask(std::make_shared());
pool.submitTask(std::make_shared());
pool.submitTask(std::make_shared());
pool.submitTask(std::make_shared());
pool.submitTask(std::make_shared());*/
getchar();
#endif
}
用户提交完任务,没有等待接收任务执行完成后的返回值。那么,线程池出了作用域就析构了,程序一瞬间就执行完毕。而此时,任务可能比较耗时,还没有执行完成。
下面我们来改造一下,线程池要等所有任务执行完之后再析构。
// 定义线程函数 线程池的所有线程从任务队列里面消费任务
void ThreadPool::threadFunc(int threadid)
{
auto lastTime = std::chrono::high_resolution_clock().now();
// while (isPoolRunning_)
for(;;)
{
std::shared_ptr task;
{
// 先获取锁
std::unique_lock lock(taskQueMtx_);
// 下面测试用的
std::cout << "tid: " << std::this_thread::get_id() << "尝试获取任务..." << std::endl;
// cached模式下,有可能已经创建了很多线程,但是空闲线程如果超过了60s,就应该把多余的线 程
// 结束回收掉(超过initThreadSize_的线程要进行回收)
// 当前时间 - 上一次线程执行完的时间 > 60s
// 每秒钟返回一次 怎么区分超时返回,还是有任务待执行返回?
// 锁+双重判断
while (taskQue_.size() == 0)
{
if (!isPoolRunning_)
{
// 2.线程正在执行任务 线程池要结束 回收线程资源
threads_.erase(threadid);
std::cout << "threadid:" << std::this_thread::get_id() << " exit" << std::endl;
// 唤醒线程池
exitCond_.notify_all();
return;
}
if (poolMode_ == PoolMode::MODE_CACHED)
{
// 条件变量 超时返回了
if (std::cv_status::timeout == notEmpty_.wait_for(lock, std::chrono::seconds(1)))
{
// 当前时间
auto now = std::chrono::high_resolution_clock().now();
// 空闲时间
auto dur = std::chrono::duration_cast(now - lastTime);
if (dur.count() >= THREAD_MAX_IDLE_TIME && curThreadSize_ > initThreadSize_)
{
// 开始回收当前线程
// 记录线程数量的相关变量的值修改
// 把线程对象从线程列表删除
threads_.erase(threadid);
curThreadSize_--;
idleThreadSize_--;
std::cout << "threadid:" << std::this_thread::get_id() << " exit" << std::endl;
return;
}
}
}
else
{
// 等待任务队列不为空 notEmpty条件
notEmpty_.wait(lock);
}
// 1.线程本身在阻塞等待 线程池要结束 回收线程资源
//if (!isPoolRunning_)
//{
// // 线程回收
// threads_.erase(threadid);
// std::cout << "threadid:" << std::this_thread::get_id() << " exit" << std::endl;
// // 唤醒线程池
// exitCond_.notify_all();
// return;
//}
}
idleThreadSize_--;
// 下面测试用的
std::cout << "tid: " << std::this_thread::get_id() << "获取任务成功..." << std::endl;
// 从任务队列获取任务
task = taskQue_.front();
taskQue_.pop();
taskSize_--;
// 如果依然有剩余任务,继续通知其他线程来获取执行任务,提高了多线程同时并发获取任务、处理任务的能力
if (taskQue_.size() > 0)
{
notEmpty_.notify_all();
}
// 取出一个任务之后,进行通知,通知其他用户可以继续提交任务
notFull_.notify_all();
}// 一个线程取完任务之后,此刻就应该把锁释放掉,让多线程可以同时并发获取任务、处理任务
// 当前线程负责执行该任务
if (task != nullptr)
{
// task->run(); // 执行任务,把任务的返回值通过setVal方法给Result
task->exec();
}
idleThreadSize_++;
lastTime = std::chrono::high_resolution_clock().now(); // 更新线程执行完任务的时间
}
}
测试:
//线程池项目.cpp:此文件包含"main"函数。程序将在此处开始并结束。
//
#include
#include
#include
using namespace std;
#include"threadpool.hpp"
/*
有些场景,线程池是希望能够获取线程执行完任务以后的返回值的
举例:
计算 1 + ...+ 30000 的和
thread1 1 + ... + 10000
thread2 10000 + ... + 20000
thread3 20000 + ... + 30000
...
main thread: 给每一个线程分配计算的区间,并等待他们计算完返回结果,合并最终的结果即可。
*/
using ulong = unsigned long long;
class MyTask : public Task
{
public:
MyTask(int begin, int end)
:begin_(begin)
, end_(end)
{}
// 问题一:怎么设计run函数的返回值,可以表示任意类型。
Any run()
{
std::cout << "tid: " << std::this_thread::get_id() << "begin!" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(3));
ulong sum = 0;
for (int i = begin_; i <= end_; i++)
sum += i;
std::cout << "tid: " << std::this_thread::get_id() << "end!" << std::endl;
return sum;
}
private:
int begin_;
int end_;
};
int main()
{
{
ThreadPool pool;
pool.setMode(PoolMode::MODE_CACHED);
pool.start(2);
Result res1 = pool.submitTask(std::make_shared(1, 100000000));
Result res2 = pool.submitTask(std::make_shared(1, 100000000));
pool.submitTask(std::make_shared(1, 100000000));
pool.submitTask(std::make_shared(1, 100000000));
pool.submitTask(std::make_shared(1, 100000000));
// ulong sum1 = res1.get().cast_();
// cout << sum1 << endl;
}
cout << "main over!" << endl;
getchar();
#if 0
{
ThreadPool pool;
// 用户自己设置线程池的工作模式
pool.setMode(PoolMode::MODE_CACHED);
pool.start(4);
srand(time(0));
// 如何设计这里的Result机制呢?
size_t begin1 = clock();
Result res1 = pool.submitTask(std::make_shared(1, 100000000));
Result res2 = pool.submitTask(std::make_shared(100000001, 200000000));
Result res3 = pool.submitTask(std::make_shared(200000001, 300000000));
pool.submitTask(std::make_shared(200000001, 300000000));
pool.submitTask(std::make_shared(200000001, 300000000));
pool.submitTask(std::make_shared(200000001, 300000000));
ulong sum1 = res1.get().cast_();
ulong sum2 = res2.get().cast_();
ulong sum3 = res3.get().cast_();
size_t end1 = clock();
// Master - Slave线程模型
// Master线程用来分解任务,然后给各个Slaver线程分配任务
// 等待各个Slaver线程执行完成任务,返回结果
// Master线程合并各个任务结果,输出
// cout << "测试结果:" << (sum1 + sum2 + sum3) << "花费时间:" << end1 - begin1 << endl;
1+...300000000正确结果
//size_t begin2 = clock();
//ulong sum = 0;
//for (int i = 1; i <= 300000000; i++)
// sum += i;
//size_t end2 = clock();
//cout << "正确结果:" << sum << "花费时间:" << end2 - begin2 << endl;
}
/*pool.submitTask(std::make_shared());
pool.submitTask(std::make_shared());
pool.submitTask(std::make_shared());
pool.submitTask(std::make_shared());
pool.submitTask(std::make_shared());
pool.submitTask(std::make_shared());
pool.submitTask(std::make_shared());
pool.submitTask(std::make_shared());
pool.submitTask(std::make_shared());
pool.submitTask(std::make_shared());*/
getchar();
#endif
}
线程池析构的时候,要等全部的任务执行完毕。