简易win32线程池实现

本文介绍了如何实现一个简单的win32线程池,通过创建多个线程并利用条件变量进行同步,当有新任务时发送信号唤醒等待的线程执行任务。线程池使用链表作为任务队列,每个任务包含一个函数及其参数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线程池大致情况: 下面的代码没用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;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值