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;
}