Linux 下C語言簡單實現線程池

C語言簡單實現線程池

0 前言

網上關於線程池的例子還是不少,簡單明瞭的倒是比較少,看了網上的資料,打算借鑑網上的一些例子,自己實現以下。

線程的概念就不多說,首先說一下多線程的好處:多線程技術主要解決處理器單元內多個線程執行的問題,它可以顯著減少處理器單元的閒置時間,增加處理器單元的吞吐能力。

那麼爲什麼又需要線程池呢?

我們知道應用程序創建一個對象,然後銷燬對象是很耗費資源的。創建線程,銷燬線程,也是如此。因此,我們就預先生成一些線程,等到我們使用的時候在進行調度,於是,一些"池化資源"技術就這樣的產生了。

 

1 線程池優點

下面使用網上資源驗證線程池如何提高服務器性能的。

我所提到服務器程序是指能夠接受客戶請求並能處理請求的程序,而不只是指那些接受網絡客戶請求的網絡服務器程序。

多線程技術主要解決處理器單元內多個線程執行的問題,它可以顯著減少處理器單元的閒置時間,增加處理器單元的吞吐能力。但如果對多線程應用不當,會增加對單個任務的處理時間。可以舉一個簡單的例子:

假設在一臺服務器完成一項任務的時間爲T

     T1 創建線程的時間

      T2 在線程中執行任務的時間,包括線程間同步所需時間

      T3 線程銷燬的時間              

顯然T = T1+T2+T3。注意這是一個極度簡化的假設。

可以看出T1,T3是多線程本身的帶來的開銷,我們渴望減少T1,T3所用的時間,從而減少T的時間。但一些線程的使用者並沒有注意到這一點,所以在程序中頻繁的創建或銷燬線程,這導致T1和T3在T中佔有相當比例。顯然這是突出了線程的弱點(T1,T3),而不是優點(併發性)。

線程池技術正是關注如何縮短或調整T1,T3時間的技術,從而提高服務器程序性能的。它把T1,T3分別安排在服務器程序的啓動和結束的時間段或者一些空閒的時間段,這樣在服務器程序處理客戶請求時,不會有T1,T3的開銷了。

線程池不僅調整T1,T3產生的時間段,而且它還顯著減少了創建線程的數目。在看一個例子:

假設一個服務器一天要處理50000個請求,並且每個請求需要一個單獨的線程完成。我們比較利用線程池技術和不利於線程池技術的服務器處理這些請求時所產生的線程總數。在線程池中,線程數一般是固定的,所以產生線程總數不會超過線程池中線程的數目或者上限(以下簡稱線程池尺寸),而如果服務器不利用線程池來處理這些請求則線程總數爲50000。一般線程池尺寸是遠小於50000。所以利用線程池的服務器程序不會爲了創建50000而在處理請求時浪費時間,從而提高效率。

這些都是假設,不能充分說明問題,下面我將討論線程池的簡單實現並對該程序進行對比測試,以說明線程技術優點及應用領域。

 

2 線程池的簡單實現

一般一個簡單線程池至少包含下列組成部分。

  1. 線程池管理器(ThreadPoolManager):用於創建並管理線程池
  2. 工作線程(WorkThread): 線程池中線程
  3. 任務接口(Task):每個任務必須實現的接口,以供工作線程調度任務的執行。
  4. 任務隊列:用於存放沒有處理的任務。提供一種緩衝機制。

 

下面是代碼:

全局文件:

/********************************** 
 * @author     wallwind@yeah.net
 * @date        2012/06/13
 * Last update: 2012/06/13
 * License:     LGPL
 * 
 **********************************/
 
 #ifndef _GLOBAL_H_
 #define _GLOBAL_H_
 
 #include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>             /* */
#include <stdarg.h>
#include <stddef.h>             /* offsetof() */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <pwd.h>
#include <grp.h>
#include <dirent.h>
#include <glob.h>
#include <sys/vfs.h>            /* statfs() */

#include <sys/uio.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sched.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>        /* TCP_NODELAY, TCP_CORK */
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/un.h>

#include <time.h>               /* tzset() */
#include <malloc.h>             /* memalign() */
#include <limits.h>             /* IOV_MAX */
#include <sys/ioctl.h>
#include <sys/sysctl.h>
#include <crypt.h>
#include <sys/utsname.h>        /* uname() */
#include <semaphore.h>

#include <sys/epoll.h>
#include <poll.h>
#include <sys/syscall.h>
#include <pthread.h>
 #endif


thread.c文件

/********************************** 
 * @author      [email protected]
 * @date        2012/06/13
 * Last update: 2012/06/13
 * License:     LGPL
 * 
 **********************************/
 
 
 
 #ifndef _THPOOL_
 #define _THPOOL_
 
 #include "global.h"
 /**
	定義一個任務節點
 **/
 typedef void* (*FUNC)(void* arg);
 
 
 typedef struct _thpool_job_t{
//	void* (*function)(void* arg);    //函數指針
	FUNC 			 function;
	void*                   arg;     //函數參數。
	struct _thpool_job_t* prev;     // 指向上一個節點
	struct _thpool_job_t* next;	    //指向下一個節點
 } thpool_job_t;
 
 /**
	定義一個工作隊列
 **/
 
typedef struct _thpool_job_queue{
	thpool_job_t*    head;            //隊列頭指針 
	thpool_job_t*    tail;			   // 隊列末尾指針
	int              jobN;					  //任務數
	sem_t*           queueSem;			  //x信號量
}thpool_jobqueue; 
 
 /**
	線程池
 **/
 
 typedef struct _thpool_t{
	pthread_t*      threads;    ////線程指針數
	int 		    threadsN;    //// 線程數
	thpool_jobqueue* jobqueue;   // 指向隊列指針
 }thpool_t;
 
 typedef struct thread_data{                            
	pthread_mutex_t *mutex_p;
	thpool_t        *tp_p;
}thread_data;

 //初始化線程池內部的線程數
thpool_t*  thpool_init(int threadN);

void thpool_thread_do(thpool_t* tp_p);

int thpool_add_work(thpool_t* tp_p, void *(*function_p)(void*), void* arg_p);

void thpool_destroy(thpool_t* tp_p);



int thpool_jobqueue_init(thpool_t* tp_p);



void thpool_jobqueue_add(thpool_t* tp_p, thpool_job_t* newjob_p);

int thpool_jobqueue_removelast(thpool_t* tp_p);

thpool_job_t* thpool_jobqueue_peek(thpool_t* tp_p);

void thpool_jobqueue_empty(thpool_t* tp_p);

 #endif

 

thread.c

/********************************** 
 * @author     [email protected]
 * @date        2012/06/13
 * Last update: 2012/06/13
 * License:     LGPL
 * 
 **********************************/
 #include "global.h"
 #include "Thread.h"
 #include <errno.h>
 
 static int thpool_keepalive = 1;
 
 /* 創建互斥量,並初始化 */
  pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; /* used to serialize queue access */
 
thpool_t*  thpool_init(int threadN)
{
	thpool_t* thpool;
	if(!threadN || threadN < 1)
		threadN = 1;
	///分配線程池內存
	thpool = (thpool_t*) malloc(sizeof(thpool_t));
	if(thpool ==NULL)
	{
		printf("malloc thpool_t error");
		return NULL;
	}
	//分配線程數
	thpool->threadsN = threadN;
	thpool->threads =(pthread_t*) malloc(threadN*sizeof(pthread_t));
	if(thpool->threads == NULL)
	{
		printf("malloc thpool->threads error");
		return NULL;
	}
	if(thpool_jobqueue_init(thpool))
		return -1;

	thpool->jobqueue->queueSem =(sem_t*)malloc(sizeof(sem_t));
	sem_init(thpool->jobqueue->queueSem,0,1);
	int t;
	for(t = 0;t< threadN ;t++)
	{
		pthread_create(&(thpool->threads[t]),NULL,(void *)thpool_thread_do,(void*)thpool);
	}
	
	return thpool;
}

void thpool_destroy(thpool_t* tp_p)
{
	int i ;
	thpool_keepalive = 0;
	
	for(i = 0;i < (tp_p->threadsN); i++)
	{
		if(sem_post(tp_p->jobqueue->queueSem))
		{
			fprintf(stderr, "thpool_destroy(): Could not bypass sem_wait()\n");
		}

	}
	if(sem_post(tp_p->jobqueue->queueSem)!=0)
	{
		fprintf(stderr, "thpool_destroy(): Could not destroy semaphore\n");
	}
	for(i = 0;i < (tp_p->threadsN); i++)
	{
		pthread_join(tp_p->threads[i],NULL);
	}
	thpool_jobqueue_empty(tp_p);
	
	free(tp_p->threads);
	free(tp_p->jobqueue->queueSem);
	free(tp_p->jobqueue);
	free (tp_p);
	
}
////對雙向隊列初始化
/* Initialise queue */
int thpool_jobqueue_init(thpool_t* tp_p){
	tp_p->jobqueue=(thpool_jobqueue*)malloc(sizeof(thpool_jobqueue));      /* MALLOC job queue */
	if (tp_p->jobqueue==NULL) return -1;
	tp_p->jobqueue->tail=NULL;
	tp_p->jobqueue->head=NULL;
	tp_p->jobqueue->jobN=0;
	return 0;
}

////
void thpool_thread_do(thpool_t* tp_p)
{
	while(thpool_keepalive ==1)
	{
		if(sem_wait(tp_p->jobqueue->queueSem)) ///線程阻塞,等待通知 直到消息隊列有數據
		{
			perror("thpool_thread_do(): Waiting for semaphore");
			exit(1);
		}
		if(thpool_keepalive)
		{
			//(void*)(*function)(void *arg);
			FUNC function;
			void* arg_buff;
			thpool_job_t*  job_p;
			
			pthread_mutex_lock(&mutex);
			 job_p = thpool_jobqueue_peek(tp_p);
			function = job_p->function;
			arg_buff = job_p->arg;
			if(thpool_jobqueue_removelast(tp_p))
				return ;
			pthread_mutex_unlock(&mutex);
			function(arg_buff);   //運行 你的方法。
			free(job_p);         ////釋放掉。
		}
		else
		{
			return ;
		}
			
	}
	return ;
}

//得到第一個隊列的一個節點
thpool_job_t* thpool_jobqueue_peek(thpool_t* tp_p)
{
	return tp_p->jobqueue->tail;
}
/////刪除隊列的最後一個節點
int thpool_jobqueue_removelast(thpool_t* tp_p)
{
	if(tp_p ==NULL)
		return -1;
	thpool_job_t* theLastJob;
	theLastJob = tp_p->jobqueue->tail;
	switch(tp_p->jobqueue->jobN)
	{
		case 0:
			return -1;
		case 1:
			tp_p->jobqueue->head =NULL;
			tp_p->jobqueue->tail =NULL;
			break;
		default:
			theLastJob->prev->next = NULL;
			tp_p->jobqueue->tail = theLastJob->prev;
				
	}
	(tp_p->jobqueue->jobN)--;
	int reval;
	sem_getvalue(tp_p->jobqueue->queueSem,&reval);
	return 0;	
}

void thpool_jobqueue_add(thpool_t* tp_p, thpool_job_t* newjob_p)
{
	newjob_p->next = NULL;
	newjob_p->prev = NULL;
	thpool_job_t* oldFirstJob;
	oldFirstJob = tp_p->jobqueue->head;
	
	switch(tp_p->jobqueue->jobN)
	{
		case 0:
			tp_p->jobqueue->head = newjob_p;
			tp_p->jobqueue->tail = newjob_p;
			break;
		default:
			oldFirstJob->prev = newjob_p;
			newjob_p->next = oldFirstJob;
			tp_p->jobqueue->head = newjob_p;
	
	}
	(tp_p->jobqueue->jobN)++;
	sem_post(tp_p->jobqueue->queueSem);
	
	int reval;
	sem_getvalue(tp_p->jobqueue->queueSem,&reval);
	return;
}

/////將消息加入線程池
int thpool_add_work(thpool_t* tp_p, void* (*function_p)(void*), void* arg_p)
{
	thpool_job_t* newjob;
	newjob = (thpool_job_t*) malloc(sizeof(thpool_job_t));
	
	if(newjob ==NULL)
	{
		fprintf(stderr, "thpool_add_work(): Could not allocate memory for new job\n");
		exit(1);
	}
	newjob ->function = function_p;
	newjob ->arg      = arg_p;
	pthread_mutex_lock(&mutex);
	thpool_jobqueue_add(tp_p,newjob);
	pthread_mutex_unlock(&mutex);     
	return 0;
}

///清空隊列
void thpool_jobqueue_empty(thpool_t* tp_p)
{
	thpool_job_t* curjob;
	curjob = tp_p->jobqueue->tail;
	
	while(tp_p->jobqueue->jobN)
	{
		tp_p->jobqueue->tail = curjob->prev;
		free (curjob);
		curjob = tp_p->jobqueue->tail;
		(tp_p->jobqueue->jobN)--;
	}
	tp_p->jobqueue->head = NULL;
	tp_p->jobqueue->tail = NULL;
}



下面是mian函數文件

/********************************** 
 * @author      [email protected]
 * @date        2012/06/13
 * Last update: 2012/06/13
 * License:     LGPL
 * 
 **********************************/

#include "global.h"
#include "Thread.h"

	void* task1()
	{
		printf("# Thread working: %u\n", (int)pthread_self());
		printf("  Task 1 running..\n");
	}


    /* Some arbitrary task 2 */
	void* task2(int a)
	{
		printf("# Thread working: %u\n", (int)pthread_self());
		printf("  Task 2 running..\n");
		printf("%d\n", a);
	}
int main()
{
	printf("~~~~~~~~~~~");
	thpool_t* thpool;
	int i;
	thpool = thpool_init(5);
	puts("Adding 20 tasks to threadpool");
	int a=54;
	for (i=0; i<20; i++){
		thpool_add_work(thpool, (void*)task1, NULL);
		thpool_add_work(thpool, (void*)task2, (void*)a);
	};


    puts("Will kill threadpool");
	thpool_destroy(thpool);
	
}


在linux下寫程序少不了makefile文件。於是我自己寫了一個比較通用的makefile文件。僅供大家參考

makefile 代碼

SRCS=$(wildcard *.c)

OBJS=$(SRCS:.c=.o)

CC=gcc

INCLUDES=-I/

LIBS=-L/ -lpthread

CCFLAGS = -g -Wall -O0

cThreadPool : $(OBJS)

        $(CC) $^ -o $@ $(INCLUDES) $(LIBS)

%.o : %.cpp

        $(CC) -c $<$(CCFLAGS)


clean:

        rm *.o

        .PHONY:clean

運行效果如下圖

./test
Created thread 0 in pool 
Created thread 1 in pool 
Created thread 2 in pool 
Created thread 3 in pool 
Adding 20 tasks to threadpool
# Thread working: 3086773136
  Task 1 running..
# Thread working: 3076283280
  Task 2 running..
54
# Thread working: 3086773136
  Task 1 running..
# Thread working: 3086773136
  Task 2 running..
54
# Thread working: 3076283280
  Task 1 running..
# Thread working: 3086773136
  Task 2 running..
54
# Thread working: 3076283280
  Task 1 running..
# Thread working: 3086773136
  Task 2 running..
54
# Thread working: 3076283280
  Task 1 running..
# Thread working: 3086773136
  Task 2 running..
54
# Thread working: 3076283280
  Task 1 running..
# Thread working: 3086773136
  Task 2 running..
54
# Thread working: 3076283280
  Task 1 running..
# Thread working: 3086773136
  Task 2 running..
54
# Thread working: 3076283280
  Task 1 running..
# Thread working: 3086773136
  Task 2 running..
54
# Thread working: 3076283280
  Task 1 running..
# Thread working: 3086773136
  Task 2 running..
54
# Thread working: 3076283280
  Task 1 running..
# Thread working: 3086773136
  Task 2 running..
54
Will kill threadpool


線程池也是參考了別人的。

 

更多文章歡迎訪問:http://blog.csdn.net/wallwind 



 

發佈了270 篇原創文章 · 獲贊 96 · 訪問量 232萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章