您的位置:首页 > 其它

Windows线程池

2017-11-08 18:06 239 查看
目录

1、线程池是什么?解决了什么问题?

2、Windows封装的线程池解析

3、自我封装实现的线程池解析

1、线程池是什么?解决了什么问题?

传统线程的工作方式:凡是用过多线程的同学应该明白,一个线程生存周期基本可以分为三部分:线程创建、线程执行(可能还会有线程同步)、线程销毁。对于我们来说,我们创建线程最期待的只是线程执行,对于线程创建与线程销毁我们并不感兴趣,因为他们不仅浪费时间而且不做事,所以一个线程真正做事的比例占 (执行)/(创建+执行+销毁)。

线程池:于是为了避免一个程序需要大量创建线程时的不必要浪费,也就是最好的去避免线程创建与线程销毁的时间浪费,此时线程池就出现了。线程池的实现就是在初始的时候创建一些线程(业界通常认为创建CPU核心数的两倍为最佳,也有说是两倍+1),创建的线程为挂起状态(就绪),当我们有任务要处理的时候,我们就激活一个就绪的线程去完成任务,完成任务后,线程又变为就绪态进行继续等待任务的到来。这样过程使得每个线程一次创建,多次使用,如果你的程序并没有多次任务处理,使得线程池中的线程长时间处于就绪态,此时就建议你直接使用一个线程就好,不必使用线程池。

结合生活说明:假如你是一个工地的小包工头(由于没多少搬砖任务,所以手下没有常工),每次有搬砖任务的时候,你都要去找一个工人(线程)来完成任务,因为工人是临时的而且你没有住处,你还得把人家送回去过夜。但是有一段时间你常有搬砖任务,那就得经常去请人->做事->送回去,忽此时你觉得这样太累了,于是你就思考要不腾个空间(线程池)出来,请几个人常工(线程池中的线程)一直在这里候着,有事就叫他们去做,不用来回跑了。这样节约了路费,没那么浪费时间,但是得长时间腾一个空间给他们住宿。

2、Windows封装的线程池解析

线程池代码示例(Windows自身封装)

#include "stdafx.h"
#include <windows.h>
#include <iostream>
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去使用就行。

实现的效果:



3、自我封装实现的线程池解析

鉴于Windows自身封装的已经足够优化了,这里进行自我封装只是方便大家学习,理解其中的思想。

封装实现过程:



1、初始化指定线程池中线程的数量,然后创建相应数量的线程,此时创建的线程为挂起状态(也就是CreateThread第三个参数为CREATE_SUSPENDED)。然后将线程的信息(包含线程句柄等)保存到一个链表中,方便调度。

2、创建一个用于管理的线程。管理任务队列是否有任务需要处理,如果有任务需要处理,此时就遍历线程池链表中是否有线程是挂起状态,如果有就将任务分配给这个线程并激活(ResumeThread)它,让他去执行。如果没有空闲的线程,就Sleep(50)等待线程处理完任务后再去遍历。

3、如果执行任务的线程完成后,再将它置为挂起状态(SuspendThread),等待有任务到来的时候再激活。

main.cpp

#include <iostream>
#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 <windows.h>
#include <list>
#include <queue>
#include <map>

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<stThreadExec> 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  windows 线程池