线程池大致情况: 下面的代码没用c++封装,可自行封装,只提供了3个函数 :创建 , 添加任务, 停止并销毁线程池;
思路:
先起N个线程 ( _beginthreadex / pthread_create) ,问题是用什么办法先让这些线程暂停 , 并且让这些线程得到通知后再
执行某个函数呢?
下面的实现是用条件变量, 每次调用添加任务就发送一个信号 , 一旦某个线程抢占到 去某个[ 队列 ]中拿取一个任务执行.
这个队列可以是一个链表,也可以是一个dequeue , 具体自己看着办. 下面的实现使用链表
那么队列中存放什么 ? 一个函数 , 一个函数参数 , 一个 next 指针 ( 链表中需要使用到)
#include "stdafx.h"
#include <process.h>
#include <Windows.h>
#define SPINCOUNT 4000 // win32下使用, unix下无视即可
//一个任务, 用于存要执行的函数
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;
}thread_pool;
unsigned int WINAPI thread_routine(void *);
//初始化pool , pool_size: 线程池大小
thread_pool * pool_create(int pool_size)
{
if (pool_size < 1)
pool_size = 1;
thread_pool * pool = (thread_pool *)malloc(sizeof(thread_pool));
if (!pool)
return NULL;
memset(pool, 0, sizeof(thread_pool));
//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);
// 表头
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);
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);
free(pTask);
pTask = NULL;
}
}
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;
//初始化
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);
}
//unix 下需要 pthread_mutex_destroy , pthread_cond_destroy
DeleteCriticalSection(&pool->cs);
free(pool);
pool = NULL;
*ptr_pool = NULL;
return 0;
}
//测试函数
void * func_in_thread(void * args)
{
printf("%s in thread : %ld , arg:%d\n", __FUNCTIONW__, GetCurrentThreadId(), *(int*)args);
//假装执行代码
Sleep(2000);
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
thread_pool * pool = pool_create(2);
Sleep(2000);
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;
}