嵌入式Linux系统编程-》线程池+面试题+源码+Makefile【第9天】

活动地址:毕业季·进击的技术er


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

猜你喜欢

转载自blog.csdn.net/m0_45463480/article/details/125492075