線程池(linux)

線程池

概念

線程池是一種多線程處理形式,處理過程中將任務添加到隊列,然後在創建線程後自動啓動這些任務。線程池線程都是後臺線程。每個線程都使用默認的堆棧大小,以默認的優先級運行,並處於多線程單元中。我們將任務添加到任務隊列中,線程池得知有任務到來後,會喚醒線程,如若所有線程都在執行任務,則線程會處理完當前任務後,在處理任務隊列中的線程。

作用

線程池可以節省我們的系統資源和時間
線程過多會帶來調度開銷,進而影響緩存局部性和整體性能。而線程池維護着多個線程,等待着監督管理者分配可併發執行的任務。這避免了在處理短時間任務時創建與銷燬線程的代價。線程池不僅能夠保證內核的充分利用,還能防止過分調度。可用線程數量應該取決於可用的併發處理器、處理器內核、內存、網絡sockets等的數量。

在創建線程池時,創建的數量也很有講究,我們不能創建的太多,會導致很多線程根本用不到而導致系統資源的浪費。創建的太少呢就會導致線程池過度繁忙,使任務不能及時的得到解決,導致任務冗雜,效率太低。

下面我們貼代碼詳細講解

/*任務結構*/  
typedef struct worker
{
	void (*process)(char *str, int socket);  // 回調函數 
	
	/* 任務中的數據 我們用字符串和socket來講解(根據實際情況可自行更換任務數據)*/
	char str[200]; 
	int socket;

	struct worker *next;                      
}create_worker;
/*線程池結構*/
typedef struct
{
	pthread_mutex_t queue_mutex;  //線程鎖
	
	pthread_cond_t queue_cond;	  //條件變量
	
	/*鏈表結構,線程池中有所等待任務*/
	create_worker *queue_head;
	
	/*是否銷燬線程池*/
	int shutdown;
	pthread_t *threadid; //在此處使用指針結構是我們根據需求創造不同的線程數量,可以進行動態分配
	
	/*線程池中允許的活動線程數目*/
	int max_thread_num;
	
	/*當前等待隊列的任務數目*/
	int cur_queue_size;
}create_pool;


static create_pool *pool = NULL; //我們使用靜態變量 全局可見

/*創建線程池*/
void pool_init(int max_thread_num)
{
	pool = (create_pool *)malloc(sizeof(create_pool)); // 構造線程池
	
	pthread_mutex_init(&(pool->queue_mutex), NULL);    //初始化鎖
	
	pthread_cond_init(&(pool->queue_cond), NULL);      //初始化條件變量
	
	pool->queue_head = NULL;    //任務爲空
	
	pool->max_thread_num = max_thread_num; //線程數量
	
	pool->cur_queue_size = 0;  //任務數量
	
	pool->shutdown = 0;
	
	/* 初始化線程池中的線程*/
	pool->threadid = (pthread_t *)malloc(sizeof(pthread_t) * max_thread_num);
	for(int i = 0; i < max_thread_num; i++)
	{
		pthread_create(&pool->threadid[i], NULL, thread_routine, NULL);
	}
}

/*向線程池中加入任務*/
void pool_add_worker(void (*process) (char *str, int socket), char *str, int socket)
{
	/*構建一個新任務*/
	create_worker *newworker = (create_worker*)malloc(sizeof(create_worker));
	
	newworker->process = process; //給新任務的回調函數賦值

	/* 任務中的數據進行賦值*/  
	memcpy(newworker->str, str, 200);
	newworker->socket = socket;
	newworker->next = NULL;
	
	pthread_mutex_lock(&pool->queue_mutex); //此處上鎖 作用 需要將任務添加到任務隊列中,需要用的共用變量size,若不鎖住保證安全,則任務數量會造成混亂,導致線程池不能正常工作。 
	
	/*將任務加入到等待隊列*/
	create_worker *member = pool->queue_head;
	if(member != NULL)
	{
		while(member->next != NULL)
			member = member->next;
		member->next = newworker;
	}
	else
		pool->queue_head = newworker;
		
	assert(pool->queue_head != NULL);
	pool->cur_queue_size++;  //將任務數量+1

	pthread_mutex_unlock(&pool->queue_mutex); //解鎖
	
	/*等待隊列有任務,喚醒一個等待的線程*/
	pthread_cond_signal(&pool->queue_cond);
}
/*銷燬線程池, 等待隊列中的任務不會在執行,但是正在運行的線程一定會把任務運行完後在退出*/
int pool_destroy()
{
	if(pool->shutdown)
		return -1;		//防止兩次調用	
	pool->shutdown = 1;
	/*喚醒所有的等待線程,線程池要銷燬了*/
	pthread_cond_broadcast(&pool->queue_cond);

	/*阻塞等待線程退出,否則就成殭屍了*/
	for(int i = 0; i < pool->max_thread_num; i++)
		pthread_join(pool->threadid[i], NULL);      //等待所有線程執行完畢再銷燬
	free(pool->threadid);
	/*銷燬等待隊列*/
	create_worker *head = NULL;
	while(pool->queue_head != NULL)
	{
		head = pool->queue_head;
		pool->queue_head = pool->queue_head->next;
		free(head);
	}

	/*銷燬條件變量和互斥量*/
	pthread_mutex_destroy(&pool->queue_mutex);
	pthread_cond_destroy(&pool->queue_cond);
	free(pool);
	pool = NULL;
}

void *thread_routine(void *arg)
{
	while(1)
	{
		pthread_mutex_lock(&pool->queue_mutex);
		/*如果等待隊列爲0並且不銷燬線程池,則處於阻塞狀態*/
		while(pool->cur_queue_size == 0 && !pool->shutdown)
		{
			printf("thread: %ld is wait\n", pthread_self());
			pthread_cond_wait(&pool->queue_cond, &pool->queue_mutex); //讓線程掛起等待,然後將鎖釋放掉,最終將線程池中所有線程都掛起,並在此阻塞住。
		}
		
		/*線程池要銷燬了*/
		if(pool->shutdown)
		{
			/*遇到break, continue, return 等跳轉語句,要記得先解鎖*/
			pthread_mutex_unlock(&pool->queue_mutex);
			printf("thread %ld will exit\n", pthread_self());
			pthread_exit(NULL);
		}
		//pthread_cond_wait()函數被喚醒後,鎖會被重新鎖住。
		//執行到這段代碼證明有線程被喚醒工作,我們打印線程id,此處相當於調試信息
		printf("thread %ld is starting to work\n", pthread_self());
		
		assert(pool->cur_queue_size != 0);
		assert(pool->queue_head != NULL);
		
		/*等待隊列長度減1,並去除鏈表中的頭元素*/
		pool->cur_queue_size--;
		create_worker *work = pool->queue_head;
		pool->queue_head = work->next;
		pthread_mutex_unlock(&pool->queue_mutex); //在此處解鎖是因爲我們的共用變量size被賦值完成,任務和隊列也已更新,在此處解鎖。

		/*調用回調函數,執行任務*/
		(work->process) (work->str, work->socket);
		free(work);
		work = NULL;
	}
}

/*回調函數*/
void process(char *str, int socket)
{
	此處寫實現任務需要的代碼
}

上述線程池用於我的聊天室項目點我感興趣的可以看一下,大家可以一起交流。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章