操作系统与网络 2019-3-11

1.继续线程池

1.1 添加四个成员函数

1.创建线程函数 bool CreateThreadPool(DWORD dwMinThreadCount=0, DWORD dwMaxThreadCount=0) (我们给最小创建的线程个数以及最多创建的线程个数都默认为0);销毁线程函数 void DestroyThreadPool() ;投递任务函数 bool PushTask(CTask* p_task) ;线程处理函数 static unsigned int _stdcall ThreadProc( void * pVoid) (与我们之前的线程处理函数不同,这是因为我们在创建线程时使用了C++库函数而没有使用stl的CreateThread函数);
2.在构造函数中对之前定义的成员进行初始化;
3.在析构函数中调用 DestroyThreadPool 函数;

CMyThreadPool::CMyThreadPool(void)
{
	m_dwMinThreadCount = 0;
	m_dwMaxThreadCount = 0;
	m_dwCurrentThreadCount = 0;
	m_dwRunThreadCount = 0;
	m_bQuitThread = true;
	m_hSemaphoreRun = 0;
}

CMyThreadPool::~CMyThreadPool(void)
{
	this->DestroyThreadPool();

	m_dwMinThreadCount = 0;
	m_dwMaxThreadCount = 0;
	m_dwCurrentThreadCount = 0;
	m_dwRunThreadCount = 0;
	m_bQuitThread = false;
	m_hSemaphoreRun = 0;
}

1.2 创建线程的函数

1.首先判断是否给定了线程的创建个数,否则需要获取当前操作系统的内核数,因为最大线程数是内核数乘以二,因此定义一个结构体: SYSTEM_INFO si ;
2.调用函数给结构体 si 中装入系统的各种参数: ::GetSystemInfo(&si) ;
3.指定最小线程个数为内核数,最大线程个数为内核数乘以二,当前所有线程个数以及正在运行的线程个数都为零;
4.若给定了线程的创建个数,则进行赋值;
5.创建信号量: m_hSemaphoreRun = ::CreateSemaphore(0, 0, m_dwMaxThreadCount, 0) (第三个参数为 m_dwMaxThreadCount 是因为我们想让该进程池更能适应各种情况);
6.循环创建线程,若创建线程成功则将当前所有线程总数进行 ++ 操作;
7.若线程一个都没有创建成功则返回false;

// 创建线程池
bool CMyThreadPool::CreateThreadPool(DWORD dwMinThreadCount, DWORD dwMaxThreadCount)		
{
	if(dwMinThreadCount == 0 && dwMaxThreadCount == 0)
	{
		SYSTEM_INFO si;								// 系统信息结构体
		::GetSystemInfo(&si);

		// 线程范围是CPU核数的2倍
		m_dwMinThreadCount = si.dwNumberOfProcessors;
		m_dwMaxThreadCount = si.dwNumberOfProcessors*2;
		m_dwCurrentThreadCount = 0;
		m_dwRunThreadCount = 0;
	}
	else
	{
		if(dwMinThreadCount > dwMaxThreadCount)
		{
			return false;
		}
		// 初始化线程的范围
		m_dwMinThreadCount = dwMinThreadCount;
		m_dwMaxThreadCount = dwMaxThreadCount;
		m_dwCurrentThreadCount = 0;
		m_dwRunThreadCount = 0;
	}

	// 创建信号量
	m_hSemaphoreRun = ::CreateSemaphore(0, 0, m_dwMaxThreadCount, 0);
	if(m_hSemaphoreRun == NULL)
		return false;

	// 创建线程
	for(DWORD i=0; i<m_dwMinThreadCount; i++)
	{
		HANDLE h_thread = (HANDLE)_beginthreadex(0, 0, &CMyThreadPool::ThreadProc, this, 0, 0);
		if(h_thread != 0)
		{
			m_dwCurrentThreadCount++;
			m_lsThread.push_back(h_thread);
		}
	}

	// 若没有创建成功一个线程则返回false
	if(m_dwCurrentThreadCount == 0)
		return false;

	return true;
}

8.函数 _beginthreadex 是由头文件 process.h 所包含的,因此需要引用头文件;
9.函数 _beginthreadex 的返回值是 uintptr_t 型的,因此需要进行强转;

1.3 完成 DestroyThreadPool 函数

1.将线程处理标记置为 false ;
2.先加锁(关键段),再将 m_quTask 任务队列中的所有元素删除,删除之后再解锁;
3.删除线程,使用迭代器;

// 销毁线程池
void CMyThreadPool::DestroyThreadPool()
{
	// 线程处理函数标记改为false
	m_bQuitThread = false;

	// 清空任务队列
	m_lockList.MyLock();
	while (m_quTask.empty() == false)
	{
		delete m_quTask.front();
		m_quTask.pop();
	}
	m_lockList.MyUnlock();

	// 删除信号量
	::CloseHandle(m_hSemaphoreRun);
	m_hSemaphoreRun = 0;

	// 删除线程
	list<HANDLE>::iterator ite = m_lsThread.begin();
	while (ite != m_lsThread.end())
	{
		if(::WaitForSingleObject(*ite, 10) == WAIT_TIMEOUT)
		{
			::TerminateThread(*ite, -1);
		}
		::CloseHandle(*ite);
		*ite = 0;
		ite = m_lsThread.erase(ite);
	}
}

1.4 完成线程处理函数

1.获取主类的指针对象;
2.进入线程循环;
3.等待信号量到来;
4.将运行的线程个数进行原子加一操作;
5.不停地在任务队列中取任务;
6.定义一个 CTask 指针用来接受任务队列中的元素;
7.加关键段锁,取任务队列中的元素,删除取了的元素,解锁;
8.运行任务,让出时间片(让出时间片是为了让cpu可以处理其他线程);
9.最后将当前运行线程个数进行原子减一操作;

// 线程处理函数
unsigned int _stdcall CMyThreadPool::ThreadProc( void * pVoid)
{
	CMyThreadPool* p_this = (CMyThreadPool*)pVoid;
	while (p_this->m_bQuitThread)
	{
		if(::WaitForSingleObject(p_this->m_hSemaphoreRun, 10) == WAIT_TIMEOUT)
			continue;

		// 每运行一个线程就自加一次
		::InterlockedIncrement(&(p_this->m_dwRunThreadCount));

		// 不停得在任务队列中取任务出来执行
		while (1)
		{
			CTask* p_task;
			p_this->m_lockList.MyLock();
			if(p_this->m_quTask.empty() == true)
			{
				p_this->m_lockList.MyUnlock();
				break;
			}
			p_task = p_this->m_quTask.front();
			p_this->m_quTask.pop();
			p_this->m_lockList.MyUnlock();

			p_task->RunTask();
			delete p_task;
			p_task = 0;
			::Sleep(0);						// 让cpu可以处理其他线程
		}

		// 每退出一个线程就自减一次
		::InterlockedDecrement(&(p_this->m_dwRunThreadCount));
	}
	return 0;
}

1.5 投递任务函数

1.首先判断给入的任务指针是否为空;
2.将任务放到任务队列中;
3.查看是否存在空闲的线程,有的话则给一个信号量,让其运行起来;
4.没有空闲线程的话,判断是否可以继续创建线程,若能够创建,则创建一个线程并给其一个信号量,让其开始运行;

// 投递任务
bool CMyThreadPool::PushTask(CTask* p_task)
{
	// 先判断给进来的任务是不是空
	if(p_task == NULL)
		return false;

	// 把任务放到任务队列中
	m_lockList.MyLock();
	m_quTask.push(p_task);
	m_lockList.MyUnlock();

	// 看看有没有空闲的线程
	if(m_dwRunThreadCount < m_dwCurrentThreadCount)
	{
		// 有空闲的线程则给其释放一个信号量
		::ReleaseSemaphore(m_hSemaphoreRun, 1, 0);
	}
	else if(m_dwCurrentThreadCount < m_dwMaxThreadCount)
	{
		// 没有空闲的线程,但是可以继续创建
		HANDLE h_thread = (HANDLE)_beginthreadex(0, 0, &CMyThreadPool::ThreadProc, this, 0, 0);
		if(h_thread != 0)
		{
			m_lsThread.push_back(h_thread);
			m_dwCurrentThreadCount++;
			// 释放信号让其开始执行
			::ReleaseSemaphore(m_hSemaphoreRun, 1, 0);
		}
	}
	else
	{
		// 没有其他情况了
	}

	return true;
}

1.6 完成测试

1.新建一个 main.cpp 文件,在其中先定义一个继承自 CTask 类的 CMyTask 类,用来作为任务,其中有 a 和 b 两个成员,在构造函数中规定为带参构造,在 RunTask 函数中将二者进行相加并输出;
2.在 main 函数中创建一个线程池对象 CMyThreadPool tp ;
3.若创建线程池成功,则循环投递5000个任务给到任务队列中;

#include "iostream"
#include "MyThreadPool.h"
using namespace std;

class CMyTask : public CTask
{
private:
	int a;
	int b;
public:
	CMyTask(int aa, int bb)
	{
		a = aa;
		b = bb;
	}
	virtual void RunTask()
	{
		cout << a+b << endl;
	}
};

int main()
{
	CMyThreadPool tp;
	if(tp.CreateThreadPool() == true)
	{
		// 给线程投递任务
		for(int i=0; i<5000; i++)
		{
			CTask* p_task = new CMyTask(i, i+1);
			if(tp.PushTask(p_task) == false)
				delete p_task;
		}
	}

	system("pause");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_42896619/article/details/88431068
今日推荐