Windows线程池
2017-11-08 18:06
239 查看
目录
1、线程池是什么?解决了什么问题?
2、Windows封装的线程池解析
3、自我封装实现的线程池解析
线程池:于是为了避免一个程序需要大量创建线程时的不必要浪费,也就是最好的去避免线程创建与线程销毁的时间浪费,此时线程池就出现了。线程池的实现就是在初始的时候创建一些线程(业界通常认为创建CPU核心数的两倍为最佳,也有说是两倍+1),创建的线程为挂起状态(就绪),当我们有任务要处理的时候,我们就激活一个就绪的线程去完成任务,完成任务后,线程又变为就绪态进行继续等待任务的到来。这样过程使得每个线程一次创建,多次使用,如果你的程序并没有多次任务处理,使得线程池中的线程长时间处于就绪态,此时就建议你直接使用一个线程就好,不必使用线程池。
结合生活说明:假如你是一个工地的小包工头(由于没多少搬砖任务,所以手下没有常工),每次有搬砖任务的时候,你都要去找一个工人(线程)来完成任务,因为工人是临时的而且你没有住处,你还得把人家送回去过夜。但是有一段时间你常有搬砖任务,那就得经常去请人->做事->送回去,忽此时你觉得这样太累了,于是你就思考要不腾个空间(线程池)出来,请几个人常工(线程池中的线程)一直在这里候着,有事就叫他们去做,不用来回跑了。这样节约了路费,没那么浪费时间,但是得长时间腾一个空间给他们住宿。
他们的使用也就是如此的简单…
本来想解释下代码的,但是思来想去也就这几个API常用,加上代码中的注释也已经足够详细了,也就不一一赘述了。
如果哪天你要使用线程池,再copy去使用就行。
实现的效果:
封装实现过程:
1、初始化指定线程池中线程的数量,然后创建相应数量的线程,此时创建的线程为挂起状态(也就是CreateThread第三个参数为CREATE_SUSPENDED)。然后将线程的信息(包含线程句柄等)保存到一个链表中,方便调度。
2、创建一个用于管理的线程。管理任务队列是否有任务需要处理,如果有任务需要处理,此时就遍历线程池链表中是否有线程是挂起状态,如果有就将任务分配给这个线程并激活(ResumeThread)它,让他去执行。如果没有空闲的线程,就Sleep(50)等待线程处理完任务后再去遍历。
3、如果执行任务的线程完成后,再将它置为挂起状态(SuspendThread),等待有任务到来的时候再激活。
main.cpp
实现效果:
CreateMyThreadPool(3) // 给线程池创建三个线程
PostTaskToPool(MyThreadFunc, (void*)i) // 将任务放入任务队列中,让线程池中的线程来执行
cThreadPool.h
cThreadPool.cpp
如有不足望多多指正,共同进步
Windows线程池及自我实现的源码:http://pan.baidu.com/s/1qXW6LFu
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 2000XP API 中C++线程池编程实现异步数据插库
- Windows 2008 Server线程池前瞻(原创)
- Windows线程池
- select + 线程池 回应服务器(windows)
- Chapter11-"windows线程池" 之 内核对象触发调用回调函数
- Windows核心编程(笔记10) 第十一章 Windows线程池 第十二章 纤程
- 一个Windows C++的线程池的实现
- 《Windows核心编程》学习笔记(11)– Windows线程池
- 一个Windows C++的线程池类实现
- windows 下一个线程池的实现
- windows 自带线程池
- 共享Windows下C++库之线程池篇
- windows下利用线程池完成多任务的分配和运行
- Chapter11-"windows线程池" 之 内核对象触发调用回调函数
- Windows线程池
- 【转】一个windows线程池实现
- windows下线程池的设计与实现
- Windows线程池Demo
- windows自带的线程池
- Windows操作系统I/O模型—笔记4(完成端口(Completion Port)模型+线程池技术)