对 简易线程池 增强内存的使用; 不再每一次malloc / free ; 把需要的内存先分配在deque中;
代码中有些许地方的更改 , 添加了deque后, 不再malloc(thread_pool) 了 也不再memset(thread_pool);
否则deque会出错;
下面2个版本, c++版本更容易实现些, 有现成的容器和function模版
版本1 , C版本:
// tpool.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <process.h>
#include <Windows.h>
#include <deque>
#define SPINCOUNT 4000
//一个任务, 用于存要执行的函数
typedef struct _task
{
//可随意定义一个在线程中执行的函数,这种函数比较万用,就暂时先这么定义;
void *(*pFunc)(void *arg);
//函数参数
void * arg;
//下一个任务
struct _task * next;
}thread_task;
typedef struct _thread_pool
{
//unix 下 pthread_mutex_t mutex; win32 下用关键段代替 mutex
CRITICAL_SECTION cs;
//unix 下 pthread_cond_t cond;
CONDITION_VARIABLE cond;
//一个任务链表 , 指向链表头 , 也可用 dequeue 替换
thread_task * queue;
//可使用数 ,可用数与链表相关, 线程将判断此变量来查看链表中是否有额外的任务
// 直到 aviliable_count == 0 && bEnd == 0 才将此线程投入睡眠
int aviliable_count;
// 是否结束 , 1 == 结束, 0 == 还在运行
short bEnd;
// 线程handler , waitforsingleobject 需要使用
HANDLE * thread_handlers;
//线程池大小
int pool_size;
// 任务存放的容器
std::deque<thread_task*> taskBuffer;
//扩张大小
int taskBufferExtend;
//容器 关键段
CRITICAL_SECTION taskBufferCs;
}thread_pool;
unsigned int WINAPI thread_routine(void *);
//初始化pool , pool_size: 线程池大小 , buffsize 容器大小
thread_pool * pool_create(int pool_size, int buffsize = 10)
{
if (pool_size < 1)
pool_size = 1;
thread_pool * pool = new thread_pool;
if (!pool)
return NULL;
//unix : pthread_mutex_init
if (InitializeCriticalSectionAndSpinCount(&pool->cs, SPINCOUNT) == 0){
printf("InitializeCriticalSectionAndSpinCount failed! err :%ld\n", GetLastError());
InitializeCriticalSection(&pool->cs);
}
//unix : pthread_cond_init
InitializeConditionVariable(&pool->cond);
if (InitializeCriticalSectionAndSpinCount(&pool->taskBufferCs, SPINCOUNT) == 0){
printf("InitializeCriticalSectionAndSpinCount failed! err :%ld\n", GetLastError());
InitializeCriticalSection(&pool->taskBufferCs);
}
// 表头
pool->queue = NULL;
//是否结束
pool->bEnd = FALSE;
//线程个数
pool->pool_size = pool_size;
//开始可用数 = 0
pool->aviliable_count = 0;
//存放handler 的地方 , unix 下可存放线程id (pthread_t), 用于后期回收(pthread_join)
pool->thread_handlers = (HANDLE*)malloc(pool_size * sizeof(HANDLE));
//创建pool_size个线程 , unix 下 把_beginthreadex 替换成 pthread_create
for (int i = 0; i < pool_size; ++i)
pool->thread_handlers[i] =(HANDLE) _beginthreadex(0, 0, thread_routine, (void*)pool, 0, 0);
int taskBuffSize = buffsize * pool_size;
pool->taskBufferExtend = buffsize;
for (int i = 0; i < taskBuffSize; ++i){
pool->taskBuffer.push_back((thread_task *)malloc(sizeof(thread_task)));
}
return pool;
}
//线程函数, 一开始都在等待, 直到此线程被通知 WakeConditionVariable
//unix 下 : pthread_cond_signal / pthread_cond_broadcast
unsigned int WINAPI thread_routine(void * args)
{
printf("thread id:%ld start!\n", GetCurrentThreadId());
thread_pool * pool = (thread_pool *)args;
while (true)
{
EnterCriticalSection(&pool->cs);
/*
阻塞,直到可用数 > 0
unix 下使用pthread_cond_wait
不论是 SleepConditionVariableCS 还是 pthread_cond_wait 都是
执行时先加锁 , 被系统唤醒后解锁对应的 cs / mutex;
要是没有通知, 则所有线程都投入睡眠
*/
while (!pool->bEnd && 0 == pool->aviliable_count)
//投入睡眠 并 LeaveCriticalSection , 如果被唤醒则EnterCriticalSection
SleepConditionVariableCS(&pool->cond, &pool->cs, INFINITE);
/*
所有线程退出的地方
需要注意的是, 当SleepConditionVariableCS返回时会加锁 cs / mutex;
否则是不会返回的,所以所以, 下面的代码中如果要退出线程,则先解锁
*/
if (pool->bEnd)
{
printf("thread id : %ld exit\n", GetCurrentThreadId());
LeaveCriticalSection(&pool->cs); // pthread_mutex_unlock
return 0;
}
//下面则是此线程将执行的任务;
//可用数减1
pool->aviliable_count--;
//获取链表中第一个任务
thread_task *pTask = pool->queue;
//指向原来的第2个任务
pool->queue = pTask->next;
//解锁
LeaveCriticalSection(&pool->cs);
//执行任务
pTask->pFunc(pTask->arg);
//清空task
memset(pTask, 0, sizeof(thread_task));
//把没用的task还回buffer中
EnterCriticalSection(&pool->taskBufferCs);
pool->taskBuffer.push_back(pTask);
LeaveCriticalSection(&pool->taskBufferCs);
}
}
int pool_submit(thread_pool * pool, void *(*pFunc)(void*arg), void *arg)
{
//创建一个任务
/*
thread_task * pTask = (thread_task*)malloc(sizeof(thread_task));
if (!pTask)
return -1;
*/
thread_task *pTask = NULL;
EnterCriticalSection(&pool->taskBufferCs);
if (!pool->taskBuffer.empty())
{
pTask = pool->taskBuffer.front();
pool->taskBuffer.pop_front();
}
else
{
pTask = (thread_task*)malloc(sizeof(thread_task));
int len = pool->taskBufferExtend - 1;
for (int i = 0; i < len; ++i)
pool->taskBuffer.push_back((thread_task*)malloc(sizeof(thread_task)));
}
LeaveCriticalSection(&pool->taskBufferCs);
//初始化
pTask->arg = arg;
pTask->pFunc = pFunc;
pTask->next = NULL;
//添加任务到 queue
EnterCriticalSection(&pool->cs);
thread_task * queue = pool->queue;
//后来的任务添加到最后
if (queue)
{
while (queue->next)
queue = queue->next;
queue->next = pTask;
}
else
pool->queue = pTask;
//可用数加1
pool->aviliable_count++;
//别忘记了
LeaveCriticalSection(&pool->cs);
//只唤醒等待的线程, 如果没有等待的线程,比如都在执行中,则无任何作用
WakeConditionVariable(&pool->cond);
return 0;
}
int pool_delete(thread_pool ** ptr_pool)
{
thread_pool * pool = *ptr_pool;
if (!pool)
return -1;
EnterCriticalSection(&pool->cs);
//防止2次调用
if (pool->bEnd){
LeaveCriticalSection(&pool->cs);
return -1;
}
pool->bEnd = 1; // 结束标记
LeaveCriticalSection(&pool->cs);
//唤醒所有线程 , pthread_cond_broadcast
WakeAllConditionVariable(&pool->cond);
//回收线程资源 ,unix : pthread_join
for (int i = 0; i < pool->pool_size; ++i)
WaitForSingleObject(pool->thread_handlers[i], INFINITE);
//释放queue
thread_task **pHeader= &pool->queue;
thread_task * entry = NULL;
while (*pHeader){
entry = *pHeader;
*pHeader = entry->next;
free(entry);
}
//释放taskbuffer
while (!pool->taskBuffer.empty()){
free(pool->taskBuffer.front());
pool->taskBuffer.pop_front();
}
//unix 下需要 pthread_mutex_destroy , pthread_cond_destroy
DeleteCriticalSection(&pool->cs);
DeleteCriticalSection(&pool->taskBufferCs);
delete pool;
pool = NULL;
*ptr_pool = NULL;
return 0;
}
//测试函数
void * func_in_thread(void * args)
{
int d = 0;
if (args)
d = *(int*)args;
printf("%s in thread : %ld , arg:%d\n", __FUNCTIONW__, GetCurrentThreadId(), d);
//假装执行代码
Sleep(2000);
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
thread_pool * pool = pool_create(2);
int arr[20] = {};
for (int i = 0; i < 20; ++i){
arr[i] = i;
pool_submit(pool, func_in_thread, arr+i);
}
getchar();
pool_delete(&pool);
return 0;
}
版本2, c++:
class TaskServer
{
private:
std::list<std::function<void()>> taskList;
CRITICAL_SECTION mutex;
CONDITION_VARIABLE cond;
HANDLE * _threadHandlers;
BOOL bEnd;
DWORD threadnum;
public:
TaskServer(DWORD thread_num) :_threadHandlers(NULL), bEnd(FALSE), threadnum(thread_num)
{
InitializeCriticalSectionAndSpinCount(&mutex, SPINCOUNT);
InitializeConditionVariable(&cond);
}
~TaskServer()
{
WaitForMultipleObjects(threadnum, _threadHandlers, TRUE, INFINITE);
for (int i = 0; i < threadnum; ++i)
CloseHandle(_threadHandlers[i]);
delete[] _threadHandlers;
DeleteCriticalSection(&mutex);
}
void addTask(const std::function<void()>& f)
{
EnterCriticalSection(&mutex);
taskList.push_back(f);
LeaveCriticalSection(&mutex);
WakeConditionVariable(&cond);
}
void start()
{
if (threadnum < 1)
threadnum = 1;
_threadHandlers = new HANDLE[threadnum];
for (int i = 0; i < threadnum; ++i)
_threadHandlers[i] = (HANDLE)_beginthreadex(0, 0, TaskServer::thread_func, this, 0, 0);
}
void stop()
{
EnterCriticalSection(&mutex);
if (bEnd){
LeaveCriticalSection(&mutex);
return;
}
bEnd = TRUE;
LeaveCriticalSection(&mutex);
WakeAllConditionVariable(&cond);
}
static unsigned int __stdcall thread_func(void * param)
{
TaskServer * pServer = (TaskServer *)param;
pServer->onRun();
return 0;
}
virtual void onRun()
{
while (true)
{
EnterCriticalSection(&mutex);
while (!bEnd && taskList.empty())
SleepConditionVariableCS(&cond, &mutex, INFINITE);
if (bEnd){
printf(" %ld end thread!\n" , GetCurrentThreadId());
LeaveCriticalSection(&mutex);
break;
}
std::function<void()> func = taskList.front();
taskList.pop_front();
LeaveCriticalSection(&mutex);
func();
}
}
};