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;
}