活动地址:毕业季·进击的技术er
嵌入式Linux系统编程-》线程池+面试题+源码
Linux系统编程告一段落
1. 工程背景
考虑这么一个情况:为应对某场景的实际需求,要在程序中创建大量线程,并且这些线程的数量和生命周期均不确定,可能方生方死,也可能常驻内存,如何在满足其要求的同时,尽可能降低系统负载?
一个基本事实是,线程的创建和销毁都是需要额外的系统资源的,如果线程的生命周期很短,那么相对于实际干活的时间,来回重复创建和销毁就显得很不划算,但也不能让线程执行完任务之后耗着不走,因此就需要一个比较科学合理的布局,来管控线程,一个比较成熟的方案是:在上述情况下,将线程放入一个类似缓冲区的池子中,对于生命周期很短的任务,那么什么时候需要线程就从池子中捞出来干活,干完活就放回池子且让线程保持整装待命,并通过精巧的设计使得池子中的线程数量可以动态地发生变化,让线程既可以应对并发性需求,又不会浪费系统资源,这种设计思路就是线程池。
线程池的基本想法,是将许多线程,放置一个池子中(实际就一个结构体),只要有任务,就将任务投入池中,这些线程们通过某些机制,及时处理这些任务。为了便于管理,线程池还应当提供诸如初始化线程池、增删线程数量、检测未完成任务的数量、检测正在执行任务的线程的数量、销毁线程池等等基本操作。
2. 逻辑框架
要让线程池好用,主要是解决如下两个问题:
如何组织线程?
如何组织任务?
3. 线程组织
从上图可以看到,线程被创建出来之后,都处于睡眠态,它们实际上是进入了条件量的等待队列中。而任务都被放入一个链表,被互斥锁保护起来。
下面是线程池里面线程们的一生:
`【1】被创建
【2】写遗书(准备好退出处理函数,防止在持有一把锁的状态中死去)
【3】试图持有互斥锁(等待任务)
【4】判断是否有任务,若无则进入条件量等待队列睡眠,若有则进入第5步
【5】从任务链表中取得一个任务
【6】释放互斥锁
【7】销毁遗书(将备用的退出处理函数弹出,避免占用内存)
【8】执行任务,完毕之后重回第2步`
线程池中的线程示例:
while(1)
{
// 写遗书(准备好退出处理函数,防止在持有一把锁的状态中死去)
pthread_cleanup_push(handler, (void *)&pool->lock);
// 试图持有互斥锁
pthread_mutex_lock(&pool->lock);
// 判断是否有任务,若无则进入条件量等待队列睡眠
while(pool->waiting_tasks == 0 && !pool->shutdown)
pthread_cond_wait(&pool->cond, &pool->lock);
// 若线程池要关闭并销毁,那么线程就解锁并退出
if(pool->waiting_tasks == 0 && pool->shutdown == true)
{
pthread_mutex_unlock(&pool->lock);
pthread_exit(NULL);
}
// 从任务链表中取得一个任务
p = pool->task_list->next;
pool->task_list->next = p->next;
pool->waiting_tasks--;
// 释放互斥锁
pthread_mutex_unlock(&pool->lock);
// 销毁遗书(将备用的退出处理函数弹出,避免占用内存)
pthread_cleanup_pop(0);
// 为避免在执行任务期间被强制终止,可先屏蔽取消指令
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
(p->do_task)(p->arg); // 执行任务
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
// 释放任务节点
free(p);
}
4. 任务组织
任务实质上是用户需要交给线程池执行的函数,为了方便线程们执行,一般做法是将函数(即函数指针)及其参数存入一个任务节点,并将节点链成一个链表。
对于任务链表,主要操作无非是:
- 设计任务节点
- 构造任务节点
- 在任务链表中增删任务节点
- 执行任务
设计任务节点:
struct node
{
void *(*task)(void *);
void *arg;
struct node *next;
};
- 构造任务节点:
void *f(void *arg)
{
// some code
}
struct node *p = malloc(sizeof(struct node));
p->task = f;
p->arg = NULL;
执行任务:
(p->task)(p->arg);
5. 示例代码
main.c
#include "thread_pool.h"
void *mytask(void *arg)
{
int n = (int)arg;
printf("[%u][%s] ==> job will be done in %d sec...\n",
(unsigned)pthread_self(), __FUNCTION__, n);
sleep(n);
printf("[%u][%s] ==> job done!\n",
(unsigned)pthread_self(), __FUNCTION__);
return NULL;
}
void *count_time(void *arg)
{
int i = 0;
while(1)
{
sleep(1);
printf("sec: %d\n", ++i);
}
}
int main(void)
{
pthread_t a;
pthread_create(&a, NULL, count_time, NULL);
// 1, initialize the pool
thread_pool *pool = malloc(sizeof(thread_pool));
init_pool(pool, 2);
// 2, throw tasks
printf("throwing 3 tasks...\n");
add_task(pool, mytask, (void *)(rand()%10));
add_task(pool, mytask, (void *)(rand()%10));
add_task(pool, mytask, (void *)(rand()%10));
// 3, check active threads number
printf("current thread number: %d\n",
remove_thread(pool, 0));
sleep(9);
// 4, throw tasks
printf("throwing another 2 tasks...\n");
add_task(pool, mytask, (void *)(rand()%10));
add_task(pool, mytask, (void *)(rand()%10));
// 5, add threads
add_thread(pool, 2);
sleep(5);
// 6, remove threads
printf("remove 3 threads from the pool, "
"current thread number: %d\n",
remove_thread(pool, 3));
// 7, destroy the pool
destroy_pool(pool);
return 0;
}
pthread_cleanup.c
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <pthread.h>
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
void *count(void *arg)
{
int i=1;
while(1)
{
sleep(1);
printf("sec: %d\n", i++);
}
}
void handler(void *arg)
{
printf("[%u] is cancelled.\n", (unsigned)pthread_self());
pthread_mutex_t *pm = (pthread_mutex_t *)arg;
pthread_mutex_unlock(pm);
}
void *routine(void *arg)
{
#ifdef CLEANUP
pthread_cleanup_push(handler, (void *)&m);
#endif
pthread_mutex_lock(&m);
printf("[%u] lock the mutex!\n", (unsigned)pthread_self());
/*
** During sleep(), if the calling thread received a cancel-
** request and HASN'T established any cleanup handlers to
** unlock the mutex, it will leave the mutex a DEAD-LOCK
** state.
*/
sleep(2);
printf("[%u]: job finished!\n", (unsigned)pthread_self());
pthread_mutex_unlock(&m);
printf("[%u] unlock the mutex!\n", (unsigned)pthread_self());
/*
** NOTE:
**
** pthread_cleanup_push() and pthread_cleanup_pop() may be
** implemented as macro that expand to text containing '{'
** and '}', respectively. For this reason, the caller must
** user them pairly and ensure that they are paired within
** a same function and at the same lexical nesting level.
*/
#ifdef CLEANUP
pthread_cleanup_pop(0);
#endif
pthread_exit(NULL);
}
int main(int argc, char **argv)
{
pthread_t t, t1, t2;
pthread_create(&t, NULL, count, NULL);
pthread_create(&t1, NULL, routine, NULL);
pthread_create(&t2, NULL, routine, NULL);
printf("[%u] ==> t1\n", (unsigned)t1);
printf("[%u] ==> t2\n", (unsigned)t2);
printf("[%u] ==> main\n", (unsigned)pthread_self());
sleep(1);
pthread_cancel(t1);
pthread_cancel(t2);
sleep(2);
pthread_mutex_lock(&m);
printf("[%u] locked the mutex!\n",
(unsigned)pthread_self());
pthread_mutex_unlock(&m);
exit(0);
}
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 *(*do_task)(void *arg);
void *arg;
struct task *next;
};
typedef struct thread_pool
{
pthread_mutex_t lock;
pthread_cond_t cond;
bool shutdown;
struct task *task_list;
pthread_t *tids;
unsigned max_waiting_tasks;
unsigned waiting_tasks;
unsigned active_threads;
}thread_pool;
bool init_pool(thread_pool *pool, unsigned int threads_number);
bool add_task(thread_pool *pool, void *(*do_task)(void *arg), void *task);
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);
#endif
thread_pool.c
#include "thread_pool.h"
void handler(void *arg)
{
printf("[%u] is ended.\n",
(unsigned)pthread_self());
pthread_mutex_unlock((pthread_mutex_t *)arg);
}
void *routine(void *arg)
{
#ifdef DEBUG
printf("[%u] is started.\n",
(unsigned)pthread_self());
#endif
thread_pool *pool = (thread_pool *)arg;
struct task *p;
while(1)
{
/*
** push a cleanup functon handler(), make sure that
** the calling thread will release the mutex properly
** even if it is cancelled during holding the mutex.
**
** NOTE:
** pthread_cleanup_push() is a macro which includes a
** loop in it, so if the specified field of codes that
** paired within pthread_cleanup_push() and pthread_
** cleanup_pop() use 'break' may NOT break out of the
** truely loop but break out of these two macros.
** see line 61 below.
*/
//================================================//
pthread_cleanup_push(handler, (void *)&pool->lock);
pthread_mutex_lock(&pool->lock);
//================================================//
// 1, no task, and is NOT shutting down, then wait
while(pool->waiting_tasks == 0 && !pool->shutdown)
{
pthread_cond_wait(&pool->cond, &pool->lock);
}
// 2, no task, and is shutting down, then exit
if(pool->waiting_tasks == 0 && pool->shutdown == true)
{
pthread_mutex_unlock(&pool->lock);
pthread_exit(NULL); // CANNOT use 'break';
}
// 3, have some task, then consume it
p = pool->task_list->next;
pool->task_list->next = p->next;
pool->waiting_tasks--;
//================================================//
pthread_mutex_unlock(&pool->lock);
pthread_cleanup_pop(0);
//================================================//
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
(p->do_task)(p->arg);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
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->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->max_waiting_tasks = MAX_WAITING_TASKS;
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;
}
#ifdef DEBUG
printf("[%u]:[%s] ==> tids[%d]: [%u] is created.\n",
(unsigned)pthread_self(), __FUNCTION__,
i, (unsigned)pool->tids[i]);
#endif
}
return true;
}
bool add_task(thread_pool *pool,
void *(*do_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->do_task = do_task;
new_task->arg = arg;
new_task->next = NULL;
//============ LOCK =============//
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++;
//=========== UNLOCK ============//
pthread_mutex_unlock(&pool->lock);
//===============================//
#ifdef DEBUG
printf("[%u][%s] ==> a new task has been added.\n",
(unsigned)pthread_self(), __FUNCTION__);
#endif
pthread_cond_signal(&pool->cond);
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");
// no threads has been created, return fail
if(actual_increment == 0)
return -1;
break;
}
actual_increment++;
#ifdef DEBUG
printf("[%u]:[%s] ==> tids[%d]: [%u] is created.\n",
(unsigned)pthread_self(), __FUNCTION__,
i, (unsigned)pool->tids[i]);
#endif
}
pool->active_threads += actual_increment;
return actual_increment;
}
int remove_thread(thread_pool *pool, unsigned int removing_threads)
{
if(removing_threads == 0)
return pool->active_threads;
int remaining_threads = pool->active_threads - removing_threads;
remaining_threads = remaining_threads > 0 ? remaining_threads : 1;
int i;
for(i=pool->active_threads-1; i>remaining_threads-1; i--)
{
errno = pthread_cancel(pool->tids[i]);
if(errno != 0)
break;
#ifdef DEBUG
printf("[%u]:[%s] ==> cancelling tids[%d]: [%u]...\n",
(unsigned)pthread_self(), __FUNCTION__,
i, (unsigned)pool->tids[i]);
#endif
}
if(i == pool->active_threads-1)
return -1;
else
{
pool->active_threads = i+1;
return i+1;
}
}
bool destroy_pool(thread_pool *pool)
{
// 1, activate all threads
pool->shutdown = true;
pthread_cond_broadcast(&pool->cond);
// 2, wait for their exiting
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]);
}
// 3, free memories
free(pool->task_list);
free(pool->tids);
free(pool);
return true;
}
Makefile
CC = gcc
CFLAGS = -O0 -Wall -g -lpthread
test:main.c thread_pool.c
$(CC) $^ -o $@ $(CFLAGS)
debug:main.c thread_pool.c
$(CC) $^ -o $@ $(CFLAGS) -DDEBUG
clean:
$(RM) .*.sw? test debug *.o
.PHONY:all clean
6.线程池接口文档
10.面试题
编写一个程序,用线程池实现并行文件或目录的拷贝。
解析
本题主要考察对线程池的实际应用,对目录进行拷贝时,可将文件复制封装为线程池任务,主线程负责递归地将目录下的所有文件打开并投入线程池,直到将所有文件拷贝结束为止。
threadpool.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
#define BUFSIZE 100
#define PATHSIZE 100
struct task
{
void *(*do_task)(void *arg);
void *arg;
struct task *next;
};
typedef struct thread_pool
{
pthread_mutex_t lock;
pthread_cond_t cond;
bool shutdown;
struct task *task_list;
pthread_t *tids;
unsigned max_waiting_tasks;
unsigned waiting_tasks;
unsigned active_threads;
}thread_pool;
bool init_pool(thread_pool *pool, unsigned int threads_number);
bool add_task(thread_pool *pool, void *(*do_task)(void *arg), void *task);
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);
#endif
threadpool.c
#include "threadpool.h"
void handler(void *arg)
{
pthread_mutex_unlock((pthread_mutex_t *)arg);
}
void *routine(void *arg)
{
#ifdef DEBUG
printf("[%u] is started.\n",
(unsigned)pthread_self());
#endif
thread_pool *pool = (thread_pool *)arg;
struct task *p;
while(1)
{
/*
** push a cleanup functon handler(), make sure that
** the calling thread will release the mutex properly
** even if it is cancelled during holding the mutex.
**
** NOTE:
** pthread_cleanup_push() is a macro which includes a
** loop in it, so if the specified field of codes that
** paired within pthread_cleanup_push() and pthread_
** cleanup_pop() use 'break' may NOT break out of the
** truely loop but break out of these two macros.
** see line 56 below.
*/
//================================================//
pthread_cleanup_push(handler, (void *)&pool->lock);
pthread_mutex_lock(&pool->lock);
//================================================//
// 1, no task, and is NOT shutting down, then wait
while(pool->waiting_tasks == 0 && !pool->shutdown)
{
pthread_cond_wait(&pool->cond, &pool->lock);
}
// 2, no task, and is shutting down, then exit
if(pool->waiting_tasks == 0 && pool->shutdown == true)
{
pthread_mutex_unlock(&pool->lock);
pthread_exit(NULL); // CANNOT use 'break';
}
// 3, have some task, then consume it
p = pool->task_list->next;
pool->task_list->next = p->next;
pool->waiting_tasks--;
//================================================//
pthread_mutex_unlock(&pool->lock);
pthread_cleanup_pop(0);
//================================================//
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
(p->do_task)(p->arg);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
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->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->max_waiting_tasks = MAX_WAITING_TASKS;
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;
}
#ifdef DEBUG
printf("[%u]:[%s] ==> tids[%d]: [%u] is created.\n",
(unsigned)pthread_self(), __FUNCTION__,
i, (unsigned)pool->tids[i]);
#endif
}
return true;
}
bool add_task(thread_pool *pool,
void *(*do_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->do_task = do_task;
new_task->arg = arg;
new_task->next = NULL;
//============ LOCK =============//
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++;
//=========== UNLOCK ============//
pthread_mutex_unlock(&pool->lock);
//===============================//
#ifdef DEBUG
printf("[%u][%s] ==> a new task has been added.\n",
(unsigned)pthread_self(), __FUNCTION__);
#endif
pthread_cond_signal(&pool->cond);
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");
// no threads has been created, return fail
if(actual_increment == 0)
return -1;
break;
}
actual_increment++;
#ifdef DEBUG
printf("[%u]:[%s] ==> tids[%d]: [%u] is created.\n",
(unsigned)pthread_self(), __FUNCTION__,
i, (unsigned)pool->tids[i]);
#endif
}
pool->active_threads += actual_increment;
return actual_increment;
}
int remove_thread(thread_pool *pool, unsigned int removing_threads)
{
if(removing_threads == 0)
return pool->active_threads;
int remaining_threads = pool->active_threads - removing_threads;
remaining_threads = remaining_threads > 0 ? remaining_threads : 1;
int i;
for(i=pool->active_threads-1; i>remaining_threads-1; i--)
{
errno = pthread_cancel(pool->tids[i]);
if(errno != 0)
break;
#ifdef DEBUG
printf("[%u]:[%s] ==> cancelling tids[%d]: [%u]...\n",
(unsigned)pthread_self(), __FUNCTION__,
i, (unsigned)pool->tids[i]);
#endif
}
if(i == pool->active_threads-1)
return -1;
else
{
pool->active_threads = i+1;
return i+1;
}
}
bool destroy_pool(thread_pool *pool)
{
// 1, activate all threads
pool->shutdown = true;
pthread_cond_broadcast(&pool->cond);
// 2, wait for their exiting
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));
}
}
// 3, free memories
free(pool->task_list);
free(pool->tids);
free(pool);
return true;
}
copy.c
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <dirent.h>
#include "thread_pool.h"
int copyfile(int fd_src, int fd_dst)
{
char buf[BUFSIZE];
int nread, nwrite, ntotal=0;
while(1)
{
bzero(buf, BUFSIZE);
nread = read(fd_src, buf, BUFSIZE);
if(nread == 0)
break;
else if(nread == -1)
{
perror("read error");
return -1;
}
char *p = buf;
while(nread > 0)
{
nwrite = write(fd_dst, p, nread);
nread -= nwrite;
p += nwrite;
ntotal += nwrite;
}
}
return ntotal;
}
void *mytask(void *arg)
{
int fd[2];
fd[0] = ((int *)arg)[0];
fd[1] = ((int *)arg)[1];
copyfile(fd[0], fd[1]);
return NULL;
}
void copydir(thread_pool *pool,
const char *dir_src, const char *dir_dst)
{
char abs_ori[PATHSIZE] = {
0};
char abs_src[PATHSIZE] = {
0};
char abs_dst[PATHSIZE] = {
0};
getcwd(abs_ori, PATHSIZE);
chdir(dir_src);
getcwd(abs_src, PATHSIZE);
chdir(abs_ori);
mkdir(dir_dst, 0777);
chdir(dir_dst);
getcwd(abs_dst, PATHSIZE);
DIR *dp_src;
dp_src = opendir(abs_src);
int fd[2];
struct dirent *ep;
struct stat *file_info;
while(1)
{
chdir(abs_src);
ep = readdir(dp_src);
if(ep == NULL)
break;
else if(!strcmp(ep->d_name, ".") ||
!strcmp(ep->d_name, ".."))
continue;
file_info = calloc(1, sizeof(struct stat));
stat(ep->d_name, file_info);
if(S_ISDIR(file_info->st_mode))
{
chdir(abs_dst);
mkdir(ep->d_name, 0777);
chdir(ep->d_name);
char path[PATHSIZE] = {
0};
getcwd(path, PATHSIZE);
chdir(abs_src);
/* copy sub-directory recursively */
copydir(pool, ep->d_name, path);
}
else if(S_ISREG(file_info->st_mode))
{
fd[0] = open(ep->d_name, O_RDONLY);
chdir(abs_dst);
fd[1] = open(ep->d_name,
O_WRONLY|O_CREAT|O_TRUNC,
0644);
add_task(pool, mytask, (void *)fd);
}
}
}
int main(int argc, char **argv)
{
if(argc != 3)
exit(0);
// 1, initialize the pool
thread_pool *pool = malloc(sizeof(thread_pool));
init_pool(pool, 3);
struct stat *file_info = calloc(1, sizeof(struct stat));
stat(argv[1], file_info);
// 2, throw tasks
if(S_ISREG(file_info->st_mode))
{
int fd[2];
fd[0] = open(argv[1], O_RDONLY);
fd[1] = open(argv[2], O_CREAT|O_WRONLY|O_TRUNC, 0644);
add_task(pool, mytask, (void *)fd);
}
else if(S_ISDIR(file_info->st_mode))
{
copydir(pool, argv[1], argv[2]);
}
else
printf("file format is NOT supported.\n");
// 3, destroy the pool
destroy_pool(pool);
return 0;
}
main.c
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <dirent.h>
#include "threadpool.h"
int copyfile(int fd_src, int fd_dst)
{
char buf[BUFSIZE];
int nread, nwrite, ntotal=0;
while(1)
{
bzero(buf, BUFSIZE);
nread = read(fd_src, buf, BUFSIZE);
if(nread == 0)
break;
else if(nread == -1)
{
perror("read error");
return -1;
}
char *p = buf;
while(nread > 0)
{
nwrite = write(fd_dst, p, nread);
nread -= nwrite;
p += nwrite;
ntotal += nwrite;
}
}
return ntotal;
}
void *mytask(void *arg)
{
int fd[2];
fd[0] = ((int *)arg)[0];
fd[1] = ((int *)arg)[1];
copyfile(fd[0], fd[1]);
return NULL;
}
void copydir(thread_pool *pool,
const char *dir_src, const char *dir_dst)
{
char abs_ori[PATHSIZE] = {
0};
char abs_src[PATHSIZE] = {
0};
char abs_dst[PATHSIZE] = {
0};
getcwd(abs_ori, PATHSIZE);
chdir(dir_src);
getcwd(abs_src, PATHSIZE);
chdir(abs_ori);
mkdir(dir_dst, 0777);
chdir(dir_dst);
getcwd(abs_dst, PATHSIZE);
DIR *dp_src;
dp_src = opendir(abs_src);
int fd[2];
struct dirent *ep;
struct stat *file_info;
while(1)
{
chdir(abs_src);
ep = readdir(dp_src);
if(ep == NULL)
break;
else if(!strcmp(ep->d_name, ".") ||
!strcmp(ep->d_name, ".."))
continue;
file_info = calloc(1, sizeof(struct stat));
stat(ep->d_name, file_info);
if(S_ISDIR(file_info->st_mode))
{
chdir(abs_dst);
mkdir(ep->d_name, 0777);
chdir(ep->d_name);
char path[PATHSIZE] = {
0};
getcwd(path, PATHSIZE);
chdir(abs_src);
/* copy sub-directory recursively */
copydir(pool, ep->d_name, path);
}
else if(S_ISREG(file_info->st_mode))
{
fd[0] = open(ep->d_name, O_RDONLY);
chdir(abs_dst);
fd[1] = open(ep->d_name,
O_WRONLY|O_CREAT|O_TRUNC,
0644);
add_task(pool, mytask, (void *)fd);
}
}
}
int main(int argc, char **argv)
{
if(argc != 3)
exit(0);
// 1, initialize the pool
thread_pool *pool = malloc(sizeof(thread_pool));
init_pool(pool, 3);
struct stat *file_info = calloc(1, sizeof(struct stat));
stat(argv[1], file_info);
// 2, throw tasks
if(S_ISREG(file_info->st_mode))
{
int fd[2];
fd[0] = open(argv[1], O_RDONLY);
fd[1] = open(argv[2], O_CREAT|O_WRONLY|O_TRUNC, 0644);
add_task(pool, mytask, (void *)fd);
}
else if(S_ISDIR(file_info->st_mode))
{
copydir(pool, argv[1], argv[2]);
}
else
printf("file format is NOT supported.\n");
// 3, destroy the pool
destroy_pool(pool);
return 0;
}
Makefile
CC = gcc
CFLAGS = -O0 -Wall -g -lpthread
copy:copy.c thread_pool.c
$(CC) $^ -o $@ $(CFLAGS)
debug:copy.c thread_pool.c
$(CC) $^ -o $@ $(CFLAGS) -DDEBUG
clean:
$(RM) .*.sw? copy debug *.o
.PHONY:all clean
结束篇总结
整套课程,都是博主用心良苦整理,是以前企业教育培训的课程,价值连城,排版、细节、内容等,都要追求精益求精,好好学习吧少年!
活动地址:毕业季·进击的技术er