首页 > 编程语言 >【并发编程十七】c++实现一个线程池

【并发编程十七】c++实现一个线程池

时间:2023-03-16 17:13:50浏览次数:40  
标签:队列 编程 c++ threadPool spTask 任务 线程 include

 

【并发编程十七】c++实现一个线程池

 

  • 简介:
    大多数系统上,若因某些任务可以与其他任务并行处理,就分别给他们配备专属的线程,则这种做法不切实际。但是只要有可能,我们还是想充分利用可调配的并发算力。线程池正好可以帮助我们达到目的:讲可同时执行的任务都提交到线程池,再将其放入任务队列中等待;随后,队列中的任务分别由某一工作线程领取并执行,执行完成后,改线程再从任务队列中取出另外一任务来执行,如此循环往复。

一、线程池原理

  • 线程池其实只是一组线程。
  • 在一般情况下,我们需要异步执行一些任务,这些任务的产生和执行是存在于程序的整个生命周期的。
  • 与其让操作系统频繁低为我们创建和销毁线程,不如创建一组在程序生命周期内不会退出的线程。
  • 为了不浪费系统的资源,我们的基本要求是当有任务需要执行时,这些线程可以自动拿到任务并执行,在没有任务时这些线程处于阻塞或者睡眠状态。

二、实现重点

  • 既然在程序生命周期内会产生很多任务,那么这些任务必须有一个存放的地方,这个地方就是队列,所以不要一提到队列就认为它是1个具体的list,队列也可以是一个全局变量或链表。
  • 这在本质尚就是生产者、消费者模式,产生任务的线程是生产者,线程池中的线程是消费者。
  • 既然会有多个线程同时操作这个队列,那么根据多线程程序的原则,我们一般需要对这个队列线程加锁。
  • 在技术上除了要解决线程池创建、向任务队列中投递任务、从任务队列中取任务并处理的问题,我们还需要做一些善后的工作:线程池的清理、退出线程池中的工作线程、清零任务队列等。

三、个人理解

以下文字可以在理解完毕线程池的代码后再来看下

  • 我们可以把一个函数(或者类的方法)绑定给一个单独的线程执行。
  • 当然,我们也可以把一个函数绑定给多个线程,每个线程中执行的函数都相同。(数据不同)
  • 执行函数相同,而数据不同?绑定任务时,需要传入this,this就是给线程中执行的不同的数据。(因为需要操作不同的数据,所以需要加锁)
  • 只不过执行的过程会判断下任务队列中是否由任务,有任务才会执行。

四、实验

  • demo
#include <iostream>
#include <atomic>
#include<thread>
#include<condition_variable>
#include<list>
#include<vector>
#include<memory>
#include<functional> // bind头文件

using namespace std;

//线程池要执行的具体的业务
class Task
{
public:
	virtual void doWork()
	{
		cout << "===================== start a work =====================\n";
	}
	virtual ~Task()
	{
		cout << "===================== finsh a work =====================\n";
	}
};

// 线程池的实现
class threadPool
{
public:
	threadPool();
	~threadPool();
	threadPool(const threadPool& rhs) = delete;
	threadPool& operator = (const threadPool& rhs) = delete;
public:
	void init(int threadNumber = 4);
	void stop();
	void addTask(Task* task);
	void clear();

	//void popTask(task* task);

private:
	void threadFunction();                     //线程池中要多个线程执行的函数(休眠函数)
private:
	vector<shared_ptr<thread>> m_threadVector; //线程池中的存线程的vector
	std::list<shared_ptr<Task>> m_taskList;    //任务队列
	mutex m_mutex;                             //因为多个线程(函数),同时操作任务队列,所以需要加函数
	condition_variable m_cv;                  //条件变量
	bool m_bRunging;                          //线程池状态

};

threadPool::threadPool() :m_bRunging(false){}

threadPool::~threadPool()
{
	clear();
}

void threadPool::init(int threadNum/*=5*/)
{
	if (threadNum <= 0)
		threadNum = 2;
	else if (threadNum > thread::hardware_concurrency())
		threadNum = thread::hardware_concurrency();

	m_bRunging = true;
	for (int i = 0; i < threadNum; i++)
	{
		shared_ptr<thread> spThread;
		//m_threadVector.push_back(thread(&threadPool::threadFunction, this));//如果m_threadVector是vector<thread>,就可以这样绑定。
		spThread.reset(new thread(std::bind(&threadPool::threadFunction, this)));
		m_threadVector.push_back(spThread);
		cout << "add a thread" << endl;
	}
}

void threadPool::threadFunction()
{
	shared_ptr<Task> spTask;
	while (true)
	{
		{
			unique_lock<mutex> guard(m_mutex);
			while (m_taskList.empty())
			{
				if (!m_bRunging)
					break;
				m_cv.wait(guard);
			}

			if (!m_bRunging)
				break;
			spTask = m_taskList.front();
			m_taskList.pop_front();
		}
		/*if (spTask == NULL)
			continue;*/

		if (spTask)
		{
			spTask->doWork();
			spTask.reset();
			cout << "exit thread and threadID=" << this_thread::get_id() << endl;
		}
		else
		{
			cout << "spTask is null\n";
		}
		
	}
	
}

void threadPool::stop()
{
	m_bRunging = false;
	m_cv.notify_all();

	for (auto& iter : m_threadVector)
	{
		if (iter->joinable())
			iter->join();
	}
}

void threadPool::addTask(Task* task)
{
	shared_ptr<Task> spTask;
	spTask.reset(task);

	{
		lock_guard<mutex> guard(mutex);
		m_taskList.push_back(spTask);
		cout << "add a task.\n";
	}
	
	m_cv.notify_one();
}

void threadPool::clear()
{
	lock_guard<mutex> guard(m_mutex);
	for (auto& iter : m_taskList)
		iter.reset();
	m_taskList.clear();
}

int main()
{
	threadPool m_threadPool;
	m_threadPool.init(55);

	Task* task = NULL;
	for (int i = 0; i < 10; i++)
	{
		task = new Task();
		m_threadPool.addTask(task);
	}

	this_thread::sleep_for(chrono::milliseconds(1000));

	m_threadPool.stop();

}
  • 输出

参考:
1、《c++并发编程实战(第二版)》安东尼.威廉姆斯 著;吴天明 译;
2、《c++服务器开发精髓》 张远龙 著;

 

标签:队列,编程,c++,threadPool,spTask,任务,线程,include
From: https://www.cnblogs.com/lidabo/p/17223333.html

相关文章