并发实践:线程池

class ThreadPool
{
	std::queue<std::function<void()>> _tasks;	// 任务队列
	std::vector<std::thread> _threadPool;		// 线程池
	int _cntThread;			// 线程数
	std::mutex _mutex;
	std::condition_variable _cv;
	std::atomic<bool> _stop;

public:
	ThreadPool(int cntThread) : _cntThread(cntThread), _stop(false)
	{
		// 线程函数
		auto ThreadFunc = [this]
		{ 
			while (true)
			{
				std::function<void()> task;
				{
					std::unique_lock<std::mutex> ul(_mutex);
					_cv.wait(ul, [this] { return (!_tasks.empty() || _stop); });

					if (_stop) break;

					task = _tasks.front();
					_tasks.pop();
				}

				// 必须在解锁后再调用
				task();
			}

			{
				std::lock_guard<std::mutex> ul(_mutex);
				std::cout << "thread " << std::this_thread::get_id() << " exit!" << std::endl;
			}
		};

		// 创建线程并启动
		for (int i = 0; i < _cntThread; i++)
		{
			std::thread t(ThreadFunc);
			_threadPool.push_back(std::move(t));
		}
	}

	~ThreadPool()
	{
		_stop = true;

		_cv.notify_all();
		for (auto iter = _threadPool.begin(); iter != _threadPool.end(); iter++)
		{
			(*iter).join();
		}
	}

	template <typename F, typename... Args>
	auto commit(F f, Args... args) -> std::future<decltype(f(args...))>
	{
		using RetType = decltype(f(args...));		// 获取返回类型
		std::shared_ptr<std::packaged_task<RetType()>> task = std::make_shared<std::packaged_task<RetType()> >(
			            std::bind(f, args...));

		std::cout << "task 引用计数:" << task.use_count() << std::endl;

		std::future<RetType> ft = task->get_future();
		{
			std::lock_guard<std::mutex> lg(_mutex);
			_tasks.push([task] { (*task)(); });
		}

		std::cout << "task 引用计数:" <<  task.use_count() << std::endl;

		_cv.notify_one();

		return ft;
	}
};
细节
std::shared_ptr<std::packaged_task<RetType()>> task = std::make_shared<std::packaged_task<RetType()> >(
			            std::bind(f, args...));

1、std::bind
利用函数适配器std::bind把实参绑定到输出函数,把真正的任务函数改造成无参函数。目的是线程内部对于任何任务,都可以用相同的调用方式来启动任务,同时无需管理传参。

2、std::packaged_task
std::packaged_task把任务及其未来的执行结果封装起来,方便任务提交者获取任务的执行情况。如果对任务的执行结果不感兴趣,可以去掉这层封装,也就不用返回std::future对象,甚至不用智能指针std::shared_ptr。

template <typename F, typename... Args>
void commit(F f, Args... args)
{
	auto task = std::bind(f, args...);
	{
		std::lock_guard<std::mutex> lg(_mutex);
		_tasks.push([task] { (task)(); });
	}

	_cv.notify_one();
}

3、std::shared_ptr
如果这里不用智能指针,task变量超出生命周期后就会被析构,会在其关联的shared state中存储一个future_error异常,错误码是broken_promise。当工作者线程取出任务并执行完成,需要把执行结果存储在shared state中时就会抛出future_error异常,错误码是promise_already_satisfied。

参考

基于C++11的线程池

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值