目录
1、线程池是什么?解决了什么问题?
2、Windows封装的线程池解析
3、自我封装实现的线程池解析
传统线程的工作方式:凡是用过多线程的同学应该明白,一个线程生存周期基本可以分为三部分:线程创建、线程执行(可能还会有线程同步)、线程销毁。对于我们来说,我们创建线程最期待的只是线程执行,对于线程创建与线程销毁我们并不感兴趣,因为他们不仅浪费时间而且不做事,所以一个线程真正做事的比例占 (执行)/(创建+执行+销毁)。
线程池:于是为了避免一个程序需要大量创建线程时的不必要浪费,也就是最好的去避免线程创建与线程销毁的时间浪费,此时线程池就出现了。线程池的实现就是在初始的时候创建一些线程(业界通常认为创建CPU核心数的两倍为最佳,也有说是两倍+1),创建的线程为挂起状态(就绪),当我们有任务要处理的时候,我们就激活一个就绪的线程去完成任务,完成任务后,线程又变为就绪态进行继续等待任务的到来。这样过程使得每个线程一次创建,多次使用,如果你的程序并没有多次任务处理,使得线程池中的线程长时间处于就绪态,此时就建议你直接使用一个线程就好,不必使用线程池。
结合生活说明:假如你是一个工地的小包工头(由于没多少搬砖任务,所以手下没有常工),每次有搬砖任务的时候,你都要去找一个工人(线程)来完成任务,因为工人是临时的而且你没有住处,你还得把人家送回去过夜。但是有一段时间你常有搬砖任务,那就得经常去请人->做事->送回去,忽此时你觉得这样太累了,于是你就思考要不腾个空间(线程池)出来,请几个人常工(线程池中的线程)一直在这里候着,有事就叫他们去做,不用来回跑了。这样节约了路费,没那么浪费时间,但是得长时间腾一个空间给他们住宿。
线程池代码示例(Windows自身封装)
#include "stdafx.h"
#include
#include
using namespace std;
//线程池的回调函数
VOID WINAPI ThreadPoolCallBack(PTP_CALLBACK_INSTANCE instance, PVOID param)
{
cout << "param:" << (int)param << "\tThread id = " << GetCurrentThreadId() << endl;
Sleep(200); // 模拟一个任务时间为100毫秒的执行
return;
}
DWORD GetNumOfProcess()// 获取CPU的核心数
{
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo); // 获取操作系统信息
return sysinfo.dwNumberOfProcessors;
}
int main()
{
PTP_POOL tPool;
tPool = CreateThreadpool(NULL); // 创建一个线程池
DWORD dwMaxThread = 3; // GetNumOfProcess() * 2 + 1;
//设置线程池参数(线程池中的线程数)
SetThreadpoolThreadMaximum(tPool, dwMaxThread); // 线程池中最多线程数
SetThreadpoolThreadMinimum(tPool, 1); // 线程池中最少线程数
TP_CALLBACK_ENVIRON tcEnv;
InitializeThreadpoolEnvironment(&tcEnv); // 初始化线程池的回调环境
SetThreadpoolCallbackPool(&tcEnv, tPool); // 给线程池分配回调环境
cout << "线程池中的线程数为:" << dwMaxThread << endl << endl;
//测试例子
for (int i = 1;i < 20;i++)
{
// 向线程池中投递一个任务
TrySubmitThreadpoolCallback(ThreadPoolCallBack, (PVOID)i, &tcEnv);
}
Sleep(100000);
return 0;
}
他们的使用也就是如此的简单…
本来想解释下代码的,但是思来想去也就这几个API常用,加上代码中的注释也已经足够详细了,也就不一一赘述了。
如果哪天你要使用线程池,再copy去使用就行。
实现的效果:
鉴于Windows自身封装的已经足够优化了,这里进行自我封装只是方便大家学习,理解其中的思想。
封装实现过程:
1、初始化指定线程池中线程的数量,然后创建相应数量的线程,此时创建的线程为挂起状态(也就是CreateThread第三个参数为CREATE_SUSPENDED)。然后将线程的信息(包含线程句柄等)保存到一个链表中,方便调度。
2、创建一个用于管理的线程。管理任务队列是否有任务需要处理,如果有任务需要处理,此时就遍历线程池链表中是否有线程是挂起状态,如果有就将任务分配给这个线程并激活(ResumeThread)它,让他去执行。如果没有空闲的线程,就Sleep(50)等待线程处理完任务后再去遍历。
3、如果执行任务的线程完成后,再将它置为挂起状态(SuspendThread),等待有任务到来的时候再激活。
main.cpp
#include
#include "ThreadPoolManager.h"
using namespace std;
void MyThreadFunc(void* param)
{
cout << "param = " << (int)param << "\tThread id = " << GetCurrentThreadId() << endl;
Sleep(300);
}
int main()
{
CreateMyThreadPool(3);
for (int i = 1; i < 20; i++)
{
PostTaskToPool(MyThreadFunc, (void*)i);
}
Sleep(100000);
return 0;
}
实现效果:
CreateMyThreadPool(3) // 给线程池创建三个线程
PostTaskToPool(MyThreadFunc, (void*)i) // 将任务放入任务队列中,让线程池中的线程来执行
cThreadPool.h
#pragma once
#include
#include
#include
#include
typedef void(*ThreadCallBack) (void* param);
enum THD_STATUS//线程状态
{
running_status, //被占用状态
free_status, //空闲状态
};
struct stThreadInfo//保存每个线程的信息
{
HANDLE hHandle; //句柄
int id; //ID 自定义的
THD_STATUS status; //线程状态
ThreadCallBack func; //回调函数(自定义的)
void* param; //参数
};
struct stThreadExec//保存线程要执行的操作
{
ThreadCallBack func;
void* param;
};
class cThreadPool
{
public:
std::queue m_threadExecQueue;//预执行的线程信息
std::map<int, stThreadInfo> m_threadInfoMap;
std::map<int, stThreadInfo>::iterator it;
HANDLE m_hThreadPMana;
public:
static cThreadPool* m_threadPool;
cThreadPool();
~cThreadPool();
cThreadPool(int maxNum);
cThreadPool(cThreadPool& threadPool){}
//单例模式
static cThreadPool* CreateMyThreadPool(int maxNum)
{
if (m_threadPool == NULL)
{
m_threadPool = new cThreadPool(maxNum);
}
return m_threadPool;
}
static cThreadPool* GetThreadPool()
{
return m_threadPool;
}
// 将处理消息加入处理队列中
void PushMsgToDealQueue(ThreadCallBack func, void* param);
//检测是否有空余线程,然后取出消息队列中的待处理消息分配给线程,让线程去执行
void AllocatTask();
//运行某个线程
void RunThreadCallback(int id);
//重置某个线程为空闲状态
void ResetThread(int id);
// 清除线程池
void CleanThreadPool();
// 处理线程 回调函数 用于处理消息
static DWORD WINAPI ThreadFunc(void* param);
// 管理线程 回调函数 用于管理消息队列,然后给处理线程分配队列中的消息
static DWORD WINAPI CheckExecMsgThread(void* param);
};
cThreadPool.cpp
#include "cThreadPool.h"
cThreadPool* cThreadPool::m_threadPool = NULL;
cThreadPool::cThreadPool()
{
m_hThreadPMana = 0;
}
cThreadPool::~cThreadPool()
{
CleanThreadPool();
}
cThreadPool::cThreadPool(int maxNum)
{
//预先创建一些线程
for (int i = 1; i <= maxNum; ++i)
{
HANDLE hHandle = CreateThread(NULL, 0, ThreadFunc, (void*)i, CREATE_SUSPENDED, NULL);
stThreadInfo st;
st.hHandle = hHandle;
st.id = i;
st.status = free_status;
m_threadInfoMap[i] = st;
}
//开一个线程不停的遍历是否在队列中有预处理的线程请求
m_hThreadPMana = CreateThread(NULL, 0, CheckExecMsgThread, NULL, 0, NULL);
}
DWORD WINAPI cThreadPool::ThreadFunc(void* param)
{
while (true)
{
cThreadPool::GetThreadPool()->RunThreadCallback((int)param);
cThreadPool::GetThreadPool()->ResetThread((int)param);
}
return 0;
}
DWORD WINAPI cThreadPool::CheckExecMsgThread(void* param)
{
while (true)
{
cThreadPool::GetThreadPool()->AllocatTask();
Sleep(50);
}
return 0;
}
void cThreadPool::PushMsgToDealQueue(ThreadCallBack func, void* param)
{
stThreadExec st;
st.func = func;
st.param = param;
m_threadExecQueue.push(st);
}
//检测是否目前有空闲线程,如果有就给任务 分配处理线程
void cThreadPool::AllocatTask()
{
if (m_threadExecQueue.size() != 0) // 是否有消息处理
{
stThreadExec itExec = m_threadExecQueue.front(); // 取出队列最前面的消息
for (it = m_threadInfoMap.begin(); it != m_threadInfoMap.end(); ++it)
{
if ((it->second).status == free_status)
{
(it->second).func = itExec.func;
(it->second).param = itExec.param;
(it->second).status = running_status;
ResumeThread((it->second).hHandle);
m_threadExecQueue.pop(); // 使消息出列
break;
}
}
}
}
void cThreadPool::RunThreadCallback(int id)
{
if (m_threadInfoMap[id].status == running_status)
{
m_threadInfoMap[id].func(m_threadInfoMap[id].param);
}
}
void cThreadPool::ResetThread(int id)
{
m_threadInfoMap[id].status = free_status;
SuspendThread(m_threadInfoMap[id].hHandle);
}
void cThreadPool::CleanThreadPool()
{
CloseHandle(m_hThreadPMana);
for (it = m_threadInfoMap.begin(); it != m_threadInfoMap.end(); ++it)
{
CloseHandle((it->second).hHandle);
}
}
如有不足望多多指正,共同进步
Windows线程池及自我实现的源码:http://pan.baidu.com/s/1qXW6LFu