《线程池:线程池资源回收策略修改》

当线程池出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
}

《线程池:线程池资源回收策略修改》_第1张图片

用户提交完任务,没有等待接收任务执行完成后的返回值。那么,线程池出了作用域就析构了,程序一瞬间就执行完毕。而此时,任务可能比较耗时,还没有执行完成。

下面我们来改造一下,线程池要等所有任务执行完之后再析构。

// 定义线程函数		线程池的所有线程从任务队列里面消费任务
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
}

《线程池:线程池资源回收策略修改》_第2张图片

线程池析构的时候,要等全部的任务执行完毕。

你可能感兴趣的:(基于可变参模板的线程池项目,线程池,C++11,C++17,c++)