Linux C系統編程:使用線程池,實現cp功能


Linux C系統編程:使用線程池,實現cp功能

線程池的定義

         什麼是線程池?簡單點說,線程池就是有一堆已經創建好了的線程,初始它們都處於空閒等待狀態,當有新的任務需要處理的時候,就從這個池子裏面取一個空閒等待的線程來處理該任務,當處理完成了就再次把該線程放回池中,以供後面的任務使用。當池子裏的線程全都處理忙碌狀態時,線程池中沒有可用的空閒等待線程,此時,根據需要選擇創建一個新的線程並置入池中,或者通知任務線程池忙,稍後再試。

 

爲什麼要用線程池?

         我們說,線程的創建和銷燬比之進程的創建和銷燬是輕量級的,但是當我們的任務需要大量進行大量線程的創建和銷燬操作時,這個消耗就會變成的相當大。比如,當你設計一個壓力性能測試框架的時候,需要連續產生大量的併發操作,這個是時候,線程池就可以很好的幫上你的忙。線程池的好處就在於線程複用,一個任務處理完成後,當前線程可以直接處理下一個任務,而不是銷燬後再創建,非常適用於連續產生大量併發任務的場合。

 

線程池工作原理

         線程池中每一個線程的工作過程如下:

 線程的工作流程

1: 線程的工作流程

         線程池的任務就在於負責這些線程的創建,銷燬和任務處理參數傳遞、喚醒和等待。

1.      創建若干線程,置入線程池

2.      任務達到時,從線程池取空閒線程

3.      取得了空閒線程,立即進行任務處理

4.      否則新建一個線程,並置入線程池,執行3

5.      如果創建失敗或者線程池已滿,根據設計策略選擇返回錯誤或將任務置入處理隊列,等待處理

6.      銷燬線程池

 

2:線程池的工作原理

 

下面是根據老師給的代碼,加以修改,實現Linux系統cp命令功能的代碼實現:

頭文件: thread_pool.h

#ifndef _THREAD_POOL_H_
#define _THREAD_POOL_H_

#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

#include <errno.h>
#include <pthread.h>

#define MAX_WAITING_TASKS	1000
#define MAX_ACTIVE_THREADS	20

//任務
struct task
{
	void *(*task)(void *arg);
	void *arg;

	struct task *next;
};

//線程池
typedef struct thread_pool
{
	pthread_mutex_t lock;//互斥鎖
	pthread_cond_t  cond;//條件變量
	struct task *task_list;//任務隊列

	pthread_t *tids;//線程id

	unsigned waiting_tasks;//等待任務
	unsigned active_threads;//

	bool shutdown;//停始狀態
}thread_pool;

struct file
{
	char srcfile[1024];
	char dstfile[1024];
};

//初始化線程池
bool
init_pool(thread_pool *pool,
          unsigned int threads_number);

//新增任務
bool
add_task(thread_pool *pool,
         void *(*task)(void *arg),
         void *arg);

//新增線程
int 
add_thread(thread_pool *pool,
           unsigned int additional_threads_number);

//移除線程
int 
remove_thread(thread_pool *pool,
              unsigned int removing_threads_number);

//銷燬線程池
bool destroy_pool(thread_pool *pool);

//處理程序
void *routine(void *arg);

//copy函數
void *(*copy)(void * arg);

#endif

線程池:thread_pool.c

#include "thread_pool.h"

//互斥鎖解鎖
void handler(void *arg)
{

	pthread_mutex_unlock((pthread_mutex_t *)arg);
}

//
void *routine(void *arg)
{
	thread_pool *pool = (thread_pool *)arg;
	struct task *p;//聲明一個任務頭節點
	printf("runtone... ...\n");

	while(1)
	{

		pthread_cleanup_push(handler, (void *)&pool->lock);//線程清理函數1
		pthread_mutex_lock(&pool->lock);//上鎖


		while(pool->waiting_tasks == 0 && !pool->shutdown)//a.非停止狀態
		{
			pthread_cond_wait(&pool->cond, &pool->lock);
		}


		if(pool->waiting_tasks == 0 && pool->shutdown == true)//b.停止狀態
		{
			pthread_mutex_unlock(&pool->lock);//互斥鎖解鎖
			pthread_exit(NULL);//退出線程
		}


		p = pool->task_list->next;//指向任務隊列下一個任務節點
		pool->task_list->next = p->next;//任務頭節點的下一個賦值給線程池的變量
		pool->waiting_tasks--;//任務等待隊列減1


		pthread_mutex_unlock(&pool->lock);//解鎖
		pthread_cleanup_pop(0);//2


		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);//線程的分離相關知識
		(p->task)(p->arg);	
		printf("runtine is running ...\n");
		pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
		free(p->arg);
		free(p);
	}

	pthread_exit(NULL);
}
//初始化線程池
bool init_pool(thread_pool *pool, unsigned int threads_number)
{
	pthread_mutex_init(&pool->lock, NULL);//互斥鎖
	pthread_cond_init(&pool->cond, NULL); //條件變量初始化,pool->cond條件變量

	pool->shutdown = false;//非關閉狀態
	pool->task_list = malloc(sizeof(struct task));//爲任務鏈表申請內存空間
	pool->tids = malloc(sizeof(pthread_t) * MAX_ACTIVE_THREADS);//爲所有線程申請內存空間

	if(pool->task_list == NULL || pool->tids == NULL)//判斷任務是否有任務、是否有線程
	{
		perror("allocate memory error");
		return false;
	}

	pool->task_list->next = NULL;

	pool->waiting_tasks = 0;
	pool->active_threads = threads_number;//當前線程個數

	int i;
	for(i=0; i<pool->active_threads; i++)//初始化所有的線程
	{
		if(pthread_create(&((pool->tids)[i]), NULL,
					routine, (void *)pool) != 0)
		{
			perror("create threads error");
			return false;
		}
	}

	return true;
}
//新增任務
bool add_task(thread_pool *pool,
			void *(*task)(void *arg), void *arg)
{
	struct task *new_task = malloc(sizeof(struct task));//爲新任務申請內存空間
	if(new_task == NULL)
	{
		perror("allocate memory error");
		return false;
	}
	//給新任務數據賦值
	new_task->task = task;
	new_task->arg = arg;
	new_task->next = NULL;
	printf("new_task init successfully!\n");
	//
	pthread_mutex_lock(&pool->lock);//互斥鎖上鎖
	if(pool->waiting_tasks >= MAX_WAITING_TASKS)//判斷等待任務數是否大於最大數
	{
		pthread_mutex_unlock(&pool->lock);

		fprintf(stderr, "too many tasks.\n");
		free(new_task);

		return false;
	}
	//把新的任務插入任務鏈表
	struct task *tmp = pool->task_list;
	while(tmp->next != NULL)
		tmp = tmp->next;//指向鏈表尾

	tmp->next = new_task;//把新任務節點插到鏈表尾
	pool->waiting_tasks++;//任務等待加1
	
	printf("insert new_task to pthread_pool tasklink tail successfully!\n");
	
	pthread_mutex_unlock(&pool->lock);//互斥鎖解鎖
	pthread_cond_signal(&pool->cond);//發送一個信號給另外一個正在處於阻塞等待狀態的線程,使其脫離阻塞狀態,繼續執行.
	
	printf("close add_task successfully!\n");
	return true;
}
//新增線程,返回進程數
int add_thread(thread_pool *pool, unsigned additional_threads)
{
	if(additional_threads == 0)
		return 0;

	unsigned total_threads =
		     pool->active_threads + additional_threads;//總線程數

	int i, actual_increment = 0;
	//對線程池中的線程進行創建
	for(i = pool->active_threads;
	    i < total_threads && i < MAX_ACTIVE_THREADS;
	    i++)
	{
		if(pthread_create(&((pool->tids)[i]),
				NULL, routine, (void *)pool) != 0)
		{
			perror("add threads error");

			if(actual_increment == 0)
				return -1;

			break;
		}
		actual_increment++; //對實際創建的進程數計數
	}

	pool->active_threads += actual_increment;//更新活躍進程總數
	return actual_increment;
}
//移除線程
int remove_thread(thread_pool *pool, unsigned int removing_threads)
{
	if(removing_threads == 0)//移除個數爲0,直接返回
		return pool->active_threads;

	int remain_threads = pool->active_threads - removing_threads;
	remain_threads = remain_threads>0 ? remain_threads:1;

	int i;
	for(i=pool->active_threads-1; i>remain_threads-1; i--)
	{
		errno = pthread_cancel(pool->tids[i]);
		if(errno != 0)
			break;
	}

	if(i == pool->active_threads-1)
		return -1;
	else
	{
		pool->active_threads = i+1;
		return i+1;
	}
}
//銷燬線程池
bool destroy_pool(thread_pool *pool)
{

	pool->shutdown = true;
	pthread_cond_broadcast(&pool->cond);

	int i;
	for(i=0; i<pool->active_threads; i++)
	{
		errno = pthread_join(pool->tids[i], NULL);
		if(errno != 0)
		{
			printf("join tids[%d] error: %s\n",
					i, strerror(errno));
		}
		else
			printf("[%u] is joined\n", (unsigned)pool->tids[i]);
		
	}

	free(pool->task_list);
	free(pool->tids);
	free(pool);

	return true;
}

主函數:copy.c

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <stdbool.h>
#include <string.h>

#include "thread_pool.h"

//拷貝普通文件,成功返回0,失敗返回-1
void *copyregfile(void *arg)
{
	struct file * dofile = (struct file *)arg;
	printf("copyregfile is running...\n");
	struct stat file_stat;/*struct stat這個結構體是用來描述一個linux系統文件系統中的文件屬性的結構*/
	stat(dofile->srcfile, &file_stat);//獲取文件的屬性

	int srcfd,dstfd;
	srcfd = open(dofile->srcfile,O_RDONLY);
	dstfd = open(dofile->dstfile,O_CREAT | O_TRUNC | O_RDWR,file_stat.st_mode);//以源文件的類型和權限創建文件

	if(srcfd == -1 || dstfd == -1)
	{
		perror("open file:");
		
		//return -1;
	}

	int nread;
	char buf[100];
	while((nread = read(srcfd,buf,100)) > 0)
	{
		if( write(dstfd,buf,nread) == -1)
		{
			break;
		}
	}

	close(dstfd);
	return NULL;
}



//拷貝目錄,成功返回0.失敗返回-1
int copydir(struct file* dofile)
{
	printf("copydir is running... \n");
	struct stat file_stat;
	stat(dofile->srcfile,&file_stat); //獲取文件的屬性
	mkdir(dofile->dstfile,file_stat.st_mode); //以源目錄的類型和目錄來創建一個目錄

	DIR *srcdir = opendir(dofile->srcfile); //打開源目錄
	struct dirent *dp;
	struct file *tmpfile = malloc(sizeof(struct file)); //對本目錄下的所有文件進行拷貝操作

	while( (dp = readdir(srcdir))!=NULL )
	{
		memset(tmpfile,0,sizeof(struct file));

		if(dp->d_name[0] == '.') //如果文件爲. 或者 .. 則跳過
		{
			continue;
		}

		sprintf(tmpfile->srcfile,"%s/%s",dofile->srcfile,dp->d_name);
		sprintf(tmpfile->dstfile,"%s/%s",dofile->dstfile,dp->d_name);
		

		struct stat tmpstat;
		stat(tmpfile->srcfile,&tmpstat);

		if(S_ISREG(tmpstat.st_mode))//如果爲普通文件,則拷貝
		{
			copyregfile(tmpfile);  //這個是任務
			printf("%s\n",tmpfile->srcfile);
		}
		else if(S_ISDIR(tmpstat.st_mode))//如果爲目錄,則遞歸
		{
			copydir(tmpfile);
			printf("%s\n",tmpfile->srcfile);
		}

	}
	
	closedir(srcdir);
	free(tmpfile); 
	return 0;
}



int main(int argc,char *argv[])
{
	printf("enter\n");
	thread_pool *pool = malloc(sizeof(thread_pool));
	init_pool(pool, 20);
	//struct file *dofile = (struct file*)arg;
	if(argc != 3)
	{
		printf("Please run : ./%s xxx yyy\n",argv[0]);
		return -1;
	}
	
	struct file dofile;
	strcpy(dofile.srcfile,argv[1]);
	strcpy(dofile.dstfile,argv[2]);
	
	struct stat srcstat;
	stat(dofile.srcfile,&srcstat);
	
	if(S_ISREG(srcstat.st_mode))//如果爲普通文件,則拷貝
	{
		add_task(pool,copyregfile,(void *)&dofile);
	}
	else if(S_ISDIR(srcstat.st_mode))//如果爲目錄,則遞歸
	{
		copydir(&dofile);
	}
	printf("current thread number: %d\n",
		remove_thread(pool, 0));	
	
			
	destroy_pool(pool);
	return 0;
}

編譯:Makefile

CC = gcc
CFLAGS = -O0 -Wall -g -lpthread

test:thread_pool.c copy.c
	$(CC) $^ -o $@ $(CFLAGS)

debug:main.c thread_pool.c
	$(CC) $^ -o $@ $(CFLAGS) -DDEBUG

clean:
	$(RM) .*.sw? test debug *.o

.PHONY:all clean

運行:
./test  源路徑/文件 目標路徑/文件


CC = gcc
CFLAGS = -O0 -Wall -g -lpthread

test:thread_pool.c copy.c
	$(CC) $^ -o $@ $(CFLAGS)

debug:main.c thread_pool.c
	$(CC) $^ -o $@ $(CFLAGS) -DDEBUG

clean:
	$(RM) .*.sw? test debug *.o

.PHONY:all clean


 


#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <stdbool.h>
#include <string.h>

#include "thread_pool.h"

//拷貝普通文件,成功返回0,失敗返回-1
void *copyregfile(void *arg)
{
	struct file * dofile = (struct file *)arg;
	printf("copyregfile is running...\n");
	struct stat file_stat;/*struct stat這個結構體是用來描述一個linux系統文件系統中的文件屬性的結構*/
	stat(dofile->srcfile, &file_stat);//獲取文件的屬性

	int srcfd,dstfd;
	srcfd = open(dofile->srcfile,O_RDONLY);
	dstfd = open(dofile->dstfile,O_CREAT | O_TRUNC | O_RDWR,file_stat.st_mode);//以源文件的類型和權限創建文件

	if(srcfd == -1 || dstfd == -1)
	{
		perror("open file:");
		
		//return -1;
	}

	int nread;
	char buf[100];
	while((nread = read(srcfd,buf,100)) > 0)
	{
		if( write(dstfd,buf,nread) == -1)
		{
			break;
		}
	}

	close(dstfd);
	return NULL;
}



//拷貝目錄,成功返回0.失敗返回-1
int copydir(struct file* dofile)
{
	printf("copydir is running... \n");
	struct stat file_stat;
	stat(dofile->srcfile,&file_stat); //獲取文件的屬性
	mkdir(dofile->dstfile,file_stat.st_mode); //以源目錄的類型和目錄來創建一個目錄

	DIR *srcdir = opendir(dofile->srcfile); //打開源目錄
	struct dirent *dp;
	struct file *tmpfile = malloc(sizeof(struct file)); //對本目錄下的所有文件進行拷貝操作

	while( (dp = readdir(srcdir))!=NULL )
	{
		memset(tmpfile,0,sizeof(struct file));

		if(dp->d_name[0] == '.') //如果文件爲. 或者 .. 則跳過
		{
			continue;
		}

		sprintf(tmpfile->srcfile,"%s/%s",dofile->srcfile,dp->d_name);
		sprintf(tmpfile->dstfile,"%s/%s",dofile->dstfile,dp->d_name);
		

		struct stat tmpstat;
		stat(tmpfile->srcfile,&tmpstat);

		if(S_ISREG(tmpstat.st_mode))//如果爲普通文件,則拷貝
		{
			copyregfile(tmpfile);  //這個是任務
			printf("%s\n",tmpfile->srcfile);
		}
		else if(S_ISDIR(tmpstat.st_mode))//如果爲目錄,則遞歸
		{
			copydir(tmpfile);
			printf("%s\n",tmpfile->srcfile);
		}

	}
	
	closedir(srcdir);
	free(tmpfile); 
	return 0;
}



int main(int argc,char *argv[])
{
	printf("enter\n");
	thread_pool *pool = malloc(sizeof(thread_pool));
	init_pool(pool, 20);
	//struct file *dofile = (struct file*)arg;
	if(argc != 3)
	{
		printf("Please run : ./%s xxx yyy\n",argv[0]);
		return -1;
	}
	
	struct file dofile;
	strcpy(dofile.srcfile,argv[1]);
	strcpy(dofile.dstfile,argv[2]);
	
	struct stat srcstat;
	stat(dofile.srcfile,&srcstat);
	
	if(S_ISREG(srcstat.st_mode))//如果爲普通文件,則拷貝
	{
		add_task(pool,copyregfile,(void *)&dofile);
	}
	else if(S_ISDIR(srcstat.st_mode))//如果爲目錄,則遞歸
	{
		copydir(&dofile);
	}
	printf("current thread number: %d\n",
		remove_thread(pool, 0));	
	
			
	destroy_pool(pool);
	return 0;
}


 


#include "thread_pool.h"

//互斥鎖解鎖
void handler(void *arg)
{

	pthread_mutex_unlock((pthread_mutex_t *)arg);
}

//
void *routine(void *arg)
{
	thread_pool *pool = (thread_pool *)arg;
	struct task *p;//聲明一個任務頭節點
	printf("runtone... ...\n");

	while(1)
	{

		pthread_cleanup_push(handler, (void *)&pool->lock);//線程清理函數1
		pthread_mutex_lock(&pool->lock);//上鎖


		while(pool->waiting_tasks == 0 && !pool->shutdown)//a.非停止狀態
		{
			pthread_cond_wait(&pool->cond, &pool->lock);
		}


		if(pool->waiting_tasks == 0 && pool->shutdown == true)//b.停止狀態
		{
			pthread_mutex_unlock(&pool->lock);//互斥鎖解鎖
			pthread_exit(NULL);//退出線程
		}


		p = pool->task_list->next;//指向任務隊列下一個任務節點
		pool->task_list->next = p->next;//任務頭節點的下一個賦值給線程池的變量
		pool->waiting_tasks--;//任務等待隊列減1


		pthread_mutex_unlock(&pool->lock);//解鎖
		pthread_cleanup_pop(0);//2


		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);//線程的分離相關知識
		(p->task)(p->arg);	
		printf("runtine is running ...\n");
		pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
		free(p->arg);
		free(p);
	}

	pthread_exit(NULL);
}
//初始化線程池
bool init_pool(thread_pool *pool, unsigned int threads_number)
{
	pthread_mutex_init(&pool->lock, NULL);//互斥鎖
	pthread_cond_init(&pool->cond, NULL); //條件變量初始化,pool->cond條件變量

	pool->shutdown = false;//非關閉狀態
	pool->task_list = malloc(sizeof(struct task));//爲任務鏈表申請內存空間
	pool->tids = malloc(sizeof(pthread_t) * MAX_ACTIVE_THREADS);//爲所有線程申請內存空間

	if(pool->task_list == NULL || pool->tids == NULL)//判斷任務是否有任務、是否有線程
	{
		perror("allocate memory error");
		return false;
	}

	pool->task_list->next = NULL;

	pool->waiting_tasks = 0;
	pool->active_threads = threads_number;//當前線程個數

	int i;
	for(i=0; i<pool->active_threads; i++)//初始化所有的線程
	{
		if(pthread_create(&((pool->tids)[i]), NULL,
					routine, (void *)pool) != 0)
		{
			perror("create threads error");
			return false;
		}
	}

	return true;
}
//新增任務
bool add_task(thread_pool *pool,
			void *(*task)(void *arg), void *arg)
{
	struct task *new_task = malloc(sizeof(struct task));//爲新任務申請內存空間
	if(new_task == NULL)
	{
		perror("allocate memory error");
		return false;
	}
	//給新任務數據賦值
	new_task->task = task;
	new_task->arg = arg;
	new_task->next = NULL;
	printf("new_task init successfully!\n");
	//
	pthread_mutex_lock(&pool->lock);//互斥鎖上鎖
	if(pool->waiting_tasks >= MAX_WAITING_TASKS)//判斷等待任務數是否大於最大數
	{
		pthread_mutex_unlock(&pool->lock);

		fprintf(stderr, "too many tasks.\n");
		free(new_task);

		return false;
	}
	//把新的任務插入任務鏈表
	struct task *tmp = pool->task_list;
	while(tmp->next != NULL)
		tmp = tmp->next;//指向鏈表尾

	tmp->next = new_task;//把新任務節點插到鏈表尾
	pool->waiting_tasks++;//任務等待加1
	
	printf("insert new_task to pthread_pool tasklink tail successfully!\n");
	
	pthread_mutex_unlock(&pool->lock);//互斥鎖解鎖
	pthread_cond_signal(&pool->cond);//發送一個信號給另外一個正在處於阻塞等待狀態的線程,使其脫離阻塞狀態,繼續執行.
	
	printf("close add_task successfully!\n");
	return true;
}
//新增線程,返回進程數
int add_thread(thread_pool *pool, unsigned additional_threads)
{
	if(additional_threads == 0)
		return 0;

	unsigned total_threads =
		     pool->active_threads + additional_threads;//總線程數

	int i, actual_increment = 0;
	//對線程池中的線程進行創建
	for(i = pool->active_threads;
	    i < total_threads && i < MAX_ACTIVE_THREADS;
	    i++)
	{
		if(pthread_create(&((pool->tids)[i]),
				NULL, routine, (void *)pool) != 0)
		{
			perror("add threads error");

			if(actual_increment == 0)
				return -1;

			break;
		}
		actual_increment++; //對實際創建的進程數計數
	}

	pool->active_threads += actual_increment;//更新活躍進程總數
	return actual_increment;
}
//移除線程
int remove_thread(thread_pool *pool, unsigned int removing_threads)
{
	if(removing_threads == 0)//移除個數爲0,直接返回
		return pool->active_threads;

	int remain_threads = pool->active_threads - removing_threads;
	remain_threads = remain_threads>0 ? remain_threads:1;

	int i;
	for(i=pool->active_threads-1; i>remain_threads-1; i--)
	{
		errno = pthread_cancel(pool->tids[i]);
		if(errno != 0)
			break;
	}

	if(i == pool->active_threads-1)
		return -1;
	else
	{
		pool->active_threads = i+1;
		return i+1;
	}
}
//銷燬線程池
bool destroy_pool(thread_pool *pool)
{

	pool->shutdown = true;
	pthread_cond_broadcast(&pool->cond);

	int i;
	for(i=0; i<pool->active_threads; i++)
	{
		errno = pthread_join(pool->tids[i], NULL);
		if(errno != 0)
		{
			printf("join tids[%d] error: %s\n",
					i, strerror(errno));
		}
		else
			printf("[%u] is joined\n", (unsigned)pool->tids[i]);
		
	}

	free(pool->task_list);
	free(pool->tids);
	free(pool);

	return true;
}


 

 

 

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