线程池

1 篇文章 0 订阅
1 篇文章 0 订阅
线程池:减少频繁创建和销毁线程造成的应用程序效率下降的问题。
编译器:VC2005
了解创建线程的两个函数:
第一个是Platform SDK: DLLs, Processes, and Threads 即 win32 SDK 平台的系统API函数
HANDLE CreateThread(  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  SIZE_T dwStackSize,  LPTHREAD_START_ROUTINE lpStartAddress,
  LPVOID lpParameter,  DWORD dwCreationFlags,  LPDWORD lpThreadId);
参数1.安全属性 该线程是否可以被继承
参数2.虚拟地址空间 一个线程默认有1M的虚拟内存,一个进程能创建线程的数量由虚拟地址空间限制。
参数3.线程函数
参数4.参数
参数5.线程状态(线程优先级0-7) 挂起 就绪 执行 
参数6.线程ID


虚拟地址内存的作用是对真实物理内存的保护,内存分配是应用程序先预定虚拟内存再映射到真实物理内存。
此函数的问题:使用CreateThread()+ 静态的CRT库(动态库时没有这个问题)可能由于使用了线面的方法,导致每个线程申请私有空间的结构,但是,在线程结束之前不会对结构体进行释放从而导致内存泄露。


第二个是Run-Time Library Reference 即c/c++运行时库函数
uintptr_t _beginthreadex( 
   void *security,
   unsigned stack_size,
   unsigned ( *start_address )( void * ),
   void *arglist,
   unsigned initflag,
   unsigned *thrdaddr 
);或者_beginthread()
参数和createthread的是一样的意思。


线程函数结束时,线程并没有被销毁,线程对象必须等到线程句柄被closehandle掉。
_beginthreadex()是对CreateThread()的封装,再调用CreateThread()之前为每个线程创建私有空间保存错误码等信息,在线程函数返回后,自动调用_endthreadex()/_endthread(),这两个方法会将函数中为每个线程的私有空间进行释放,从而避免内存泄露。




介绍一个函数 ::GetTickCount() 线程运行到此处的时间, 在任务的前后分别放一个,两个返回值之差就是完成任务的时间,可以检查线程池的效率。


线程池的设计:
一.傻瓜式线程池:创建一堆线程,从不退出、不休息。(造成CPU的严重浪费,甚至可能导致主机卡死)
二.要解决傻瓜式线程池的问题需要满足:
1.不需要频繁的创建和销毁线程。(创建一组线程不退出)
2.当线程不需要工作的时候,不能死循环浪费CPU时间。(当线程没任务时,将线程挂起)
3.当有任务需要线程池处理时,需要马上有线程开始工作。(发信号通知,需要线程间同步)
线程间同步的方法:互斥量(1对1)、临界区(1)、事件(1对不定量)、信号量(1对n),所以选择信号量合适。




#pragma once


#ifndef _INCLUDE_THREAD_POOL_H_
#define _INCLUDE_THREAD_POOL_H_
#include <list>


namespace THREAD_POOL
{
//任务基类
class ITask
{
public:
BOOL virtual DealTask()=0;
};
//线程池类
class CThreadPool
{
public:
CThreadPool();
~CThreadPool();
//初始化线程池的功能 和 关闭线程池的功能
BOOL InitThreadPool(int iMinThreadCount,int iMaxThreadCount,int iTaskCount);//任务数量
void ColseThreadPool();


//投递任务
BOOL PostTask(ITask* pTask);

//创建信号量
HANDLE m_Semaphore;
private:
//任务链表 用指针对象 而不是对象集合 
std::list<ITask*> m_TaskList;


int m_iMinThreadCount; 
int m_iMaxThreadCount;
//一共创建了多少线程
int m_iCreateThreadCount;
//正在工作的线程
int m_iWorkThreadCount;
//任务队列的容量
int m_iTaskCount;


//临界区 任务队列锁
CRITICAL_SECTION m_Section;
//线程函数
static unsigned int WINAPI ThreadProc(LPVOID LpParam);
//静态函数没有this指针
void ThreadProc();
ITask* GetTask()
{


//队列里有任务了 要发送信号
::EnterCriticalSection(&m_Section);


ITask* pTask = NULL;
std::list<ITask*>::iterator it = m_TaskList.begin();

//放入的任务取出后 再取时 it变为空的无效指针 
//对it间接引用 
if (it != m_TaskList.end())
{
//说明取到了任务
pTask = *it;
m_TaskList.pop_front();
}


//任务被取出 释放信号量
::LeaveCriticalSection(&m_Section);
return pTask;
}
};
}
#endif








using namespace THREAD_POOL;


CThreadPool::CThreadPool():m_Semaphore(NULL)
,m_iMinThreadCount(0)
,m_iMaxThreadCount(0)
,m_iCreateThreadCount(0)
,m_iWorkThreadCount(0)
,m_iTaskCount(0)
{
//要初始化值类成员
}
CThreadPool::~CThreadPool()
{

ColseThreadPool();
}
BOOL CThreadPool::InitThreadPool(int iMinThreadCount,int iMaxThreadCount,int iTaskCount)
{
//校验参数
if(iMinThreadCount>iMaxThreadCount||iMinThreadCount<0||iTaskCount<0)
{
return FALSE;
}


//创建信号量  多个线程共同操作信号量 要加临界区
::InitializeCriticalSection(&m_Section);
m_Semaphore = ::CreateSemaphore(NULL,0,iMaxThreadCount,NULL);
if (!m_Semaphore)
{
return FALSE;
}


//创建线程
for(int i=0;i<iMinThreadCount;i++) 
{
::InterlockedIncrement((long*)&m_iCreateThreadCount);
HANDLE hThread = (HANDLE)::_beginthreadex(NULL,0,ThreadProc,this,0,NULL);
if (!hThread)
{
::InterlockedDecrement((long*)&m_iCreateThreadCount);
return FALSE;
}
}

//保存数据
m_iMinThreadCount = iMinThreadCount;
m_iMaxThreadCount = iMaxThreadCount;
m_iTaskCount = iTaskCount;
m_iWorkThreadCount = 0;


return TRUE;


}
void CThreadPool::ColseThreadPool()
{
//安全函数 要保证可以多次调用
if (m_Semaphore)
{
::CloseHandle(m_Semaphore);
m_Semaphore = NULL;
}
m_TaskList.clear();
//这个位置这样写的话会产生内存泄露  在任务管理器中进程中会神奇的发现 内存项一点点的增长
//很好玩 
//下边是回收空间的代码
//std::list<ITask*> m_TaskList;
//std::list<ITask*>::iterator it = m_TaskList.begin();
//while (it!=m_TaskList.end())
//{
// delete *it++;
//}
m_iMinThreadCount = 0;
m_iMaxThreadCount = 0;
}


unsigned int WINAPI CThreadPool::ThreadProc(LPVOID LpParam)
{
CThreadPool*pThis = (CThreadPool*)LpParam;
pThis->ThreadProc();


return 1l;
}


void CThreadPool::ThreadProc()
{
while(1)
{
//等待信号
::WaitForSingleObject(m_Semaphore,INFINITE);//一直等
//等到工作后 要将创建线程 变为 工作线程
::InterlockedIncrement((long*)&m_iWorkThreadCount);


//要循环处理队列里的任务
ITask* pTask = NULL;


while((pTask = GetTask())!=NULL)
{
pTask->DealTask();
delete pTask;
}
//任务处理完 要将工作线程变回创建线程
::InterlockedDecrement((long*)&m_iWorkThreadCount);
}
//减少创建线程数量
::InterlockedDecrement((long*)&m_iCreateThreadCount);
}
//投递任务
BOOL CThreadPool::PostTask(ITask* pTask)
{
if (!pTask)
{
return FALSE;
}
::EnterCriticalSection(&m_Section);
//在临界区内 向任务队列添加任务
m_TaskList.push_back(pTask);
::LeaveCriticalSection(&m_Section);


//如果 创建的线程有没工作的 发送信号 让其工作

if (m_iCreateThreadCount > m_iWorkThreadCount)
{
::ReleaseSemaphore(m_Semaphore,1,NULL);

}
else
{
//如果创建的线程都工作了 需要创建新的线程
if(m_iCreateThreadCount < m_iMaxThreadCount)
{
::InterlockedIncrement((long*)&m_iCreateThreadCount);
HANDLE hThread = (HANDLE)::_beginthreadex(NULL,0,ThreadProc,this,0,NULL);
if (!hThread)
{
::InterlockedDecrement((long*)&m_iCreateThreadCount);
return FALSE;
}


//给新创建的线程发信号 开始工作
::ReleaseSemaphore(m_Semaphore,1,NULL);

}
}
//::InterlockedIncrement((long*)&m_iWorkThreadCount);


return TRUE;
}




可以通过下班的代码进行测试
//加法任务  继承任务基类
class CAddTask:public ITask
{
public:
CAddTask(int nOne,int nTwo):m_nOne(nOne),m_nTwo(nTwo)
{


}
BOOL DealTask()
{
cout<< m_nOne + m_nTwo <<endl;
return TRUE;
}
private:
int m_nOne;
int m_nTwo;
};


int main()
{
CThreadPool threadpool;
threadpool.InitThreadPool(2,10,1000);


//投递任务
//ITask* pTask = new CAddTask(1,1);
for (int i=0;i<100000000;i++)
{
threadpool.PostTask(new CAddTask(1,1));


}
//threadpool.PostTask(pTask);
//阻塞一下
while (1)
{
::Sleep(10);
}
return 0;
}


上面的代码可以进行改进:
任务队列只有一个,由于线程间的同步,同一时间只有一个线程能对队列进行操作,其他线程只能在等待,这样线程的工作效率不高。
所以,可以拆分资源,各线程间不打扰,也就最大可能的提高了线程的工作效率。


改进方法:1:自己建立任务队列(队列的循环数组实现),代码量会减少很多
 2:会不会觉得这几行代码的锁太多了?加锁和解锁的过程太多会浪费时间,所以 将锁包含在任务队列里




队列和锁在下边,把前边的改一改换一换就好了。。。。。。。。。。。
//队列/
#pragma once
#ifndef THREAD_TASK_QUEUE_
#define THREAD_TASK_QUEUE_


#include <Windows.h>
#include "MyLock.h"
namespace TASK_QUEUE
{
//队列的数组实现
template<typename T,int MAX_SIZE = 1000>
class CQueue
{
public:
CQueue();
~CQueue();


//初始化任务队列
BOOL InitQueue(int taskcount);
//关闭任务队列
void UnInitQueue();
//放入一个任务到队列
BOOL push_queue(T* pNode);
//从队列中取出一个任务
BOOL pop_queue(T*& pNode);


private:
int m_iHead;//循环数组的头
int m_iEnd;//循环数组的尾
int m_iMaxlength;//循环数组的长度
T** m_TaskArr;//动态数组
BOOL m_run;


CMyLock m_oLock;
};
//构造
template<typename T,int MAX_SIZE>
CQueue<T,MAX_SIZE>::CQueue()
:m_TaskArr(NULL)
,m_iHead(0)
,m_iEnd(0)
,m_iMaxlength(0)
,m_run(FALSE)
{


}
//析构
template<typename T,int MAX_SIZE>
CQueue<T,MAX_SIZE>::~CQueue()
{
UnInitQueue();
}
//初始化队列
template<typename T,int MAX_SIZE>
BOOL CQueue<T,MAX_SIZE>::InitQueue(int taskcount)
{
int QueueCount=min(MAX_SIZE,taskcount+1);
//记录长度
m_iMaxlength = QueueCount;
//创建数组
m_TaskArr=new T*[m_iMaxlength];


//队列开启标记
m_run = TRUE;
return TRUE;
}
//回收
template<typename T,int MAX_SIZE>
void CQueue<T,MAX_SIZE>::UnInitQueue()
{
m_run = FALSE;
::Sleep(10);
for (int i = 0;i<m_iMaxlength;i++)
{
if (m_TaskArr[i])
{
delete m_TaskArr[i];
m_TaskArr[i] = NULL;
}
}
if (m_TaskArr)
{
delete[] m_TaskArr;
}


}
//入队
template<typename T,int MAX_SIZE>
BOOL CQueue<T,MAX_SIZE>::push_queue(T* pNode)
{
if (pNode == NULL)
{
return FALSE;
}
m_oLock.Lock();
int index = (m_iHead+1)%m_iMaxlength;
if (index == m_iEnd)
{
//队满
m_oLock.UnLock();
return FALSE;
}
m_TaskArr[m_iHead] = pNode;
m_iHead = (m_iHead+1)%m_iMaxlength;
m_oLock.UnLock();
return TRUE;
}
//出队
template<typename T,int MAX_SIZE>
BOOL CQueue<T,MAX_SIZE>::pop_queue(T*& pNode)
{


m_oLock.Lock();
//如果队是空的
if (m_iHead == m_iEnd)
{
return FALSE;
}
m_oLock.UnLock();
pNode = m_TaskArr[m_iEnd];
m_TaskArr[m_iEnd] = NULL;
m_iEnd = (m_iEnd+1)%m_iMaxlength;


m_oLock.UnLock();
return TRUE;
}




}
#endif
锁的封装/
#pragma once


#ifndef THREAD_TASK_LOCK_
#define THREAD_TASK_LOCK_




#include <Windows.h>
class CMyLock
{
public:
//构造中创建临界区
CMyLock()
{
::InitializeCriticalSection(&m_Section);
}
//加锁进入临界区
void Lock()
{
::EnterCriticalSection(&m_Section);
}
//解锁离开临界区
void UnLock()
{
::LeaveCriticalSection(&m_Section);


}
//析构中销毁临界区
~CMyLock()
{
::DeleteCriticalSection(&m_Section);
}
private:
CRITICAL_SECTION m_Section;
};
#endif
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值