c语言通过cond和mutex实现一个高性能线程池

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/FlayHigherGT/article/details/85012749

首先说明一下这篇文章其实和我另一篇文章很像,只是另一篇只有一个线程,只要讲述的是cond和mutex的使用,上面是基于c++实现的,封装的稍微好一些,路径:

https://blog.csdn.net/FlayHigherGT/article/details/83830956

而下面是基于c实现的线程池,模仿大神写的,没用任何c++的特性以及库,通过链表装载任务,代码中的注释以及很明确了,欢迎大家一起来学习。

#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

/*采用链表装载任务*/
typedef struct STask
{
	struct STask *pNext;
	int nValue;
}*TASK;

struct SThreadInfo 
{
	int bThreadRunning;
	int nThreadNum;
	int nTaskNum;
	TASK pTask;

	/*这个指针里面存储n个线程句柄(n为线程池大小)*/
	pthread_t *pThreadId;

	pthread_mutex_t hMutex;
	pthread_cond_t hCond;
};

struct SThreadInfo g_sThreadInfo;

/*初始化线程池*/
static void ThreadPoolInit(int nThreadNum);
/*线程池线程的执行函数*/
static void *ThreadPoolRoutine(void *ThreadParam);
/*线程池做链表中的某一个任务*/
static void ThreadPoolDoTask(TASK pTask);
/*取出链表中的头一个任务*/
static TASK ThreadPoolRetrieveTask();
/*销毁线程池*/
static void ThreadPoolDestroy();

static void ThreadPoolInit(int nThreadNum)
{
	/*线程池默认是个线程*/
	if (nThreadNum <= 0)
		nThreadNum = 10;

	pthread_mutex_init(&g_sThreadInfo.hMutex, NULL);
	pthread_cond_init(&g_sThreadInfo.hCond, NULL);

	g_sThreadInfo.nThreadNum = nThreadNum;
	g_sThreadInfo.bThreadRunning = 1;
	g_sThreadInfo.nTaskNum = 0;

	/*给每个handle分配空间*/
	g_sThreadInfo.pThreadId = (pthread_t*)malloc(sizeof(pthread_t*) * nThreadNum);

	/*创建n个线程*/
	for (int i = 0; i < nThreadNum; ++i)
		pthread_create(&g_sThreadInfo.pThreadId[i], NULL, ThreadPoolRoutine, NULL);
}

/*取任务,取链表头*/
static TASK ThreadPoolRetrieveTask()
{
	TASK pHead = g_sThreadInfo.pTask;
	if (pHead)
	{
		g_sThreadInfo.pTask = pHead->pNext;
		--g_sThreadInfo.nTaskNum;
		printf("retrieve a task, task value is [%d]\n", pHead->nValue);
		return pHead;
	}

	printf("no task\n");
	return NULL;
}

static void ThreadPoolDoTask(TASK pTask)
{
	if (!pTask)
		return;

	/*此时可以做任务,睡一秒模拟做任务*/
	sleep(1);

	printf("task value is [%d]\n", pTask->nValue);

	free(pTask);
}

static void ThreadPoolAddTask(TASK pTask)
{
	if (!pTask)
		return;

	pthread_mutex_lock(&g_sThreadInfo.hMutex);

	TASK pHead = g_sThreadInfo.pTask;

	if (!pHead)
	{
		g_sThreadInfo.pTask = pTask;
	} else
	{
		while (pHead->pNext != NULL)
		{
			pHead = pHead->pNext;
		}
		pHead->pNext = pTask;
	}

	++g_sThreadInfo.nTaskNum;

	/*关于先解锁还是先发信号,貌似先发信号的说法多一点,这里采用先发信号*/
	pthread_cond_signal(&g_sThreadInfo.hCond);
	pthread_mutex_unlock(&g_sThreadInfo.hMutex);
}

static void *ThreadPoolRoutine(void *ThreadParam)
{
	printf("thread NO.%d start\n", (int)pthread_self());

	while (g_sThreadInfo.bThreadRunning)
	{
		TASK pCurrentTask = NULL;

		pthread_mutex_lock(&g_sThreadInfo.hMutex);

		while (g_sThreadInfo.nTaskNum <= 0)
		{
			pthread_cond_wait(&g_sThreadInfo.hCond, &g_sThreadInfo.hMutex);

			/*收到signal信号之后,第一个能执行下去的也即抢到锁的,
			 *抢到锁之后其他线程会在此处卡死,本线程执行如果线程
			 *池收到结束信号,本线程会退出拿任务执行,并结束。第
			 *一个拿到锁的本线程nTaskNum肯定是大于0的。解锁之后,
			 *其他线程谁先抢到锁谁就退出,但是貌似会执行空任务。
			 */
			if (!g_sThreadInfo.bThreadRunning)
				break;
		}

		/*本线程第一个抢到锁,正常执行任务解锁,第二个抢到锁的线程
		 *发现nTaskNum是空的,那当然还是在while循环里面,继续wait,
		 *这边就能体现为啥一定要用while而不是if的真正原因了。
		 */
		if (g_sThreadInfo.nTaskNum > 0)
		{
			pCurrentTask = ThreadPoolRetrieveTask();

			pthread_mutex_unlock(&g_sThreadInfo.hMutex);

			ThreadPoolDoTask(pCurrentTask);
		} else
		{
			pthread_mutex_unlock(&g_sThreadInfo.hMutex);
		}
	}

	printf("thread NO.%d exit\n", (int)pthread_self());
}


static void ThreadPoolDestroy()
{
	g_sThreadInfo.bThreadRunning = 0;

	/*这个不需要加锁,因为这个一旦执行,所有的wait都不会等待了,
	 *即使执行的时候不在wait,之后继续wait也不会等待了
	 */
	pthread_cond_broadcast(&g_sThreadInfo.hCond);

	for (int i = 0; i < g_sThreadInfo.nThreadNum; ++i)
		pthread_join(g_sThreadInfo.pThreadId[i], NULL);

	free(g_sThreadInfo.pThreadId);

	pthread_mutex_destroy(&g_sThreadInfo.hMutex);
	pthread_cond_destroy(&g_sThreadInfo.hCond);
}

int main(int argc, char* argv[])
{
	/*初始化线程池*/
	ThreadPoolInit(10);
	
	/*添加任务*/
	TASK pTask = NULL;
	for (int i = 0; i < 100; ++i)
	{
		pTask = (TASK)malloc(sizeof(struct STask));
		pTask->nValue = i + 1;
		pTask->pNext = NULL;
		printf("add task, task value [%d]\n", pTask->nValue);
		ThreadPoolAddTask(pTask);
	}

	/*销毁线程池*/
	ThreadPoolDestroy();

	return 0;
}

猜你喜欢

转载自blog.csdn.net/FlayHigherGT/article/details/85012749