常用的设计网站,网站论坛怎么做,设计方案英语,网站首页图片轮播Linux系统编程系列#xff08;16篇管饱#xff0c;吃货都投降了#xff01;#xff09; 1、Linux系统编程系列之进程基础 2、Linux系统编程系列之进程间通信(IPC)-信号 3、Linux系统编程系列之进程间通信(IPC)-管道 4、Linux系统编程系列之进程间通信-IPC对象 5、Linux系统… Linux系统编程系列16篇管饱吃货都投降了 1、Linux系统编程系列之进程基础 2、Linux系统编程系列之进程间通信(IPC)-信号 3、Linux系统编程系列之进程间通信(IPC)-管道 4、Linux系统编程系列之进程间通信-IPC对象 5、Linux系统编程系列之进程间通信-消息队列 6、Linux系统编程系列之进程间通信-共享内存 7、Linux系统编程系列之进程间通信-信号量组 8、Linux系统编程系列之守护进程 9、Linux系统编程系列之线程 10、Linux系统编程系列之线程属性 11、Linux系统编程系列之互斥锁和读写锁 12、Linux系统编程系列之线程的信号处理 13、Linux系统编程系列之POSIX信号量 14、Linux系统编程系列之条件变量 15、Linux系统编程系列之死锁 16、 Linux系统编程系列之线程池 一、什么是线程池 线程池就是将许多线程放置在一个池子中实际就是一个结构体只要有任务就将任务投入池中这些线程们通过某些机制及时处理这些任务一旦处理完后又重新回到池子中重新接收新任务。为了便于管理线程池还应当提供诸如初始化线程池增删线程数量检测未完成任务的数据检测正在执行任务的线程的数量、销毁线程池等等基本操作。
二、特性 1、更好的满足并发性需求 2、更加节省系统资源降低负载 3、避免每次需要执行任务时都创建新的线程 4、可以限制并发线程数量帮助控制应用程序的性能和稳定性 核心思想通过精巧的设计使得池子中的线程数量可以动态地发生变化让线程既可以应对并发性需求又不会浪费系统资源
三、使用场景 用来应对某种场景要在程序中创建大量线程并且这些线程的数量和生命周期均不确定可能方生方死也可能常驻内存请看下面举例。 1、处理大量密集型任务 线程池可以处理多个任务从而减少了创建和销毁线程的开销提高了系统性能 2、服务端应用程序 线程池可以帮助服务端应用程序同时处理多个客户端请求 3、I/O 密集型应用程序 线程池可以处理 I/O 操作允许系统使用空闲时间进行其他任务处理 4、Web 应用程序 线程池可以处理来自 Web 客户端的请求从而提高了 Web 应用程序的性能 总之任何需要处理大量任务或需要同时处理多个请求的应用程序都可以使用线程池来提高性能和效率。
四、设计思想 设计某个东西一定要有个目标这里是要求设计一个线程池首先要明白线程池的作用然后根据其作用来展开设计。线程池是通过让线程的数量进行动态的变化来及时处理接受的任务所以核心就是线程和任务。可以将问题转换为如何组织线程和组织任务借鉴别人的一幅图 1、如何组织线程 从上图可以看出线程被创建出来之后都处于睡眠态它们实际上是进入了条件变量的等待队列中而任务都被放入一个链表被互斥锁保护起来 线程的生命周期如下这里省略程序执行的流程图 1、线程被创建 2、预防死锁准备好退出处理函数防止在持有一把锁的状态中死去 3、尝试持有互斥锁等待任务 4、判断是否有任务若无则进入条件变量等待队列睡眠若有则进入第5步 5、从任务链表中取得一个任务 6、释放互斥锁 7、弹出退出处理函数避免占用内存 8、执行任务 9、执行任务完成后重新回到第2步 线程的生命周期其实就是线程要做的事情把上面的第2步到第8步写成一个线程的例程函数。 2、如何组织任务 所谓的任务就是一个函数回想一下创建一条线程时是不是要指定一个线程例程函数。这里的线程池做法是将函数准确的说是函数指针及其参数存入一个任务节点并将节点链接成一个链表。所以现在的问题变成如何设计一个任务链表 一个链表的操作有创建节点增加节点删除节点查询节点这几步。由于这里是任务节点就不需要查询了当然也可以查询。所以现在的问题转换为任务节点如何设计 任务节点的数据域主要有函数指针和函数参数指针域就是一个简单任务结点指针也可以是双向循环链表。 设计分析到这里基本上有了框架了前面买了这么多关子下面就来个重头戏。其实前面主要是想描述一种设计思想遇到新的东西如何设计它首先要明白设计的目的和作用然后找出核心点最后就是把核心点进行不断的分析推理一步步转换为最基本的问题。学过项目管理的同学应该听过在范围管理里有个叫工作分解结构的东西这里跟那个差不多。
五、案例 实现一个线程池并完成线程池的基本操作演示 thread_pool.h线程池头文件 #ifndef _THREAD_POOL_H
#define _THREAD_POOL_H#include stdio.h
#include stdlib.h
#include errno.h
#include string.h
#include pthread.h#define MAX_WAITING_TASKS 1000 // 最大的等待任务数量
#define MAX_ACTIVE_THREADS 20 // 最多的活跃线程数量// 任务节点
typedef struct task
{void *(*do_task) (void *arg); // 函数指针void *arg; // 函数参数struct task *next; // 指向下一个任务节点
}task;// 线程池的管理节点
typedef struct thread_pool
{pthread_mutex_t task_list_lock; // 任务列表互斥锁保证同一个时间只有一条线程进行访问pthread_cond_t task_list_cond; // 任务列表条件变量保证没有任务时线程进入睡眠task *task_list; // 任务列表pthread_t *tids; // 存放线程号的数组int shutdown; // 线程池销毁的开关unsigned int max_waiting_tasks; // 最大等待的任务数量unsigned int waiting_tasks; // 正在等待被处理的任务数量unsigned int active_threads; // 正在活跃的线程数}thread_pool;// 初始化线程池
int init_pool(thread_pool *pool, unsigned int threads_number);// 往线程池中添加任务
int add_task(thread_pool *pool, void *(*do_task)(void *arg), void *arg);// 往线程池中添加线程
int add_thread(thread_pool *pool, unsigned int additional_threads);// 从线程池中删除线程
int remove_thread(thread_pool *pool, unsigned int removing_threads);// 查询线程池中线程数量
int total_thread(thread_pool *pool);// 销毁线程池
int destroy_pool(thread_pool *pool);// 线程的例程函数
void *routine(void *arg);#endif // _THREAD_POOL_H thread_pool.c线程池实现文件 #include thread_pool.h// 初始化线程池
int init_pool(thread_pool *pool, unsigned int threads_number)
{if(threads_number 1){printf(warning: threads_number must bigger than 0\n);return -1;}if(threads_number MAX_ACTIVE_THREADS){printf(warning: threads number is bigger than MAX_ACTIVE_THREADS(%u)\n, MAX_ACTIVE_THREADS);return -1;}pthread_mutex_init(pool-task_list_lock, NULL); // 初始化互斥锁pthread_cond_init(pool-task_list_cond, NULL); // 初始化条件变量pool-task_list calloc(1, sizeof(task)); // 申请一个任务头节点if(!pool-task_list){printf(error: task_list calloc fail\n);return -1;}pool-task_list-next NULL; // 让任务头节点指向NULLpool-tids calloc(MAX_ACTIVE_THREADS, sizeof(pthread_t)); // 申请堆数组用来存放线程号if(!pool-tids){printf(error: tids calloc fail\n);return -1;}pool-shutdown 0; // 关闭线程池销毁开发pool-waiting_tasks 0; // 当前等待任务为0pool-max_waiting_tasks MAX_WAITING_TASKS; // 初始化最大等待任务数量pool-active_threads threads_number; // 初始化活跃的线程数// 根据活跃的线程数创建对应数量的线程for(int i 0; i pool-active_threads; i){// 需要判断线程是否创建失败// 线程号用线程号数组线程属性设置为默认的例程函数是routine函数参数是线程池errno pthread_create(pool-tids[i], NULL, routine,(void*)pool);if(errno ! 0){perror(error: pthread_create fail);return -1;}}return 1;
}// 往线程池中添加任务
int add_task(thread_pool *pool, void *(*do_task)(void *arg), void *arg)
{if(!pool){printf(warning: thread_pool is null\n);return -1;}// 如果当前等待执行的任务数超过最大等待的任务数量则退出if(pool-waiting_tasks pool-max_waiting_tasks){printf(warning: task_list is full, too many list\n);return -1;}// 尝试给新的任务节点申请空间task *new_task (task*)malloc(sizeof(task));if(!new_task){printf(error: task malloc fail\n);return -1;}// 初始化任务节点new_task-do_task do_task;new_task-arg arg;new_task-next NULL;// 把任务节点添加到任务列表最后面// 1、先找到任务列表末尾task *tmp NULL;for(tmp pool-task_list; tmp-next; tmp tmp-next);// 2、上锁pthread_mutex_lock(pool-task_list_lock);// 3、添加新任务到任务列表末尾同时当前等待任务数1tmp-next new_task;pool-waiting_tasks;// 4、解锁pthread_mutex_unlock(pool-task_list_lock);// 唤醒一个正在条件变量中睡眠等待的线程取执行任务pthread_cond_signal(pool-task_list_cond);return 1;
}// 往线程池中添加线程返回实际添加的线程数量
int add_thread(thread_pool *pool, unsigned int additional_threads)
{if(!pool){printf(warning: thread_pool is null\n);return -1;}if(additional_threads 0){return 0;}// 期望活跃的线程数 目前活跃的线程数 期望添加的线程数unsigned int total_threads pool-active_threads additional_threads;// 如果超过最大的活跃线程数就打印警告if(total_threads MAX_ACTIVE_THREADS){printf(warning: add too many threads\n);}int actual_add_threads 0;for(int i pool-active_threads; i total_threads i MAX_ACTIVE_THREADS; i){// 需要判断线程是否创建失败// 线程号用线程号数组线程属性设置为默认的例程函数是routine函数参数是线程池errno pthread_create(pool-tids[i], NULL, routine,(void*)pool);if(errno ! 0){perror(error: pthread_create fail);// 如果一个都没有创建成功就直接返回if(actual_add_threads 0){return -1;}// 如果成功创建了多个线程但是本次创建失败就跳出循环break;}else{actual_add_threads; // 线程创建成功就1}}// 更新线程池中活跃的线程数pool-active_threads actual_add_threads;return actual_add_threads; // 返回实际添加的线程
}// 从线程池中删除线程返回实际删除线程的数量
int remove_thread(thread_pool *pool, unsigned int removing_threads)
{if(!pool){printf(warning: thread_pool is null\n);return -1;}if(removing_threads 0){return pool-active_threads;}// 如果要删除的线程数大于线程池中活跃的线程数则打印警告if(removing_threads pool-active_threads){printf(warning: remove too many threads\n);}// 剩余数量 活跃数量 - 删除的目标数 int remaining_threads pool-active_threads - removing_threads;// 目的是为了让线程池中最少保留有一个线程可以用于执行任务remaining_threads remaining_threads 0 ? remaining_threads : 1;int actual_remove_threads 0;;// 循环取消线程直到等于期望线程数for(int i pool-active_threads-1; i remaining_threads-1; i--){errno pthread_cancel(pool-tids[i]);if(errno ! 0){printf([%ld] cancel error: %s\n, pool-tids[i], strerror(errno));break;}else{actual_remove_threads;}}// 更新线程池中活跃的线程数量pool-active_threads - actual_remove_threads;return actual_remove_threads; // 返回实际删除的线程
}// 查询线程池中线程数量
int total_thread(thread_pool *pool)
{return pool-active_threads;
}// 销毁线程池
int destroy_pool(thread_pool *pool)
{if(!pool){printf(warning: thread_pool is null\n);return -1;}pool-shutdown 1; // 启动线程池销毁开关pthread_cond_broadcast(pool-task_list_cond); // 唤醒所有在线程池中的线程// 循环等待所有线程退出for(int 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([%ld] is joined\n, pool-tids[i]);}}// 头删法从头一个个的删除任务节点for(task *p pool-task_list-next; p; p pool-task_list){pool-task_list-next p-next; // 修改首元节点free(p);}free(pool-task_list);free(pool-tids);free(pool);return 1;
}// 线程的取消函数
void pthread_cancel_handler(void *arg)
{printf([%ld] is cancel\n, pthread_self());pthread_mutex_unlock((pthread_mutex_t*)arg); // 解锁
}// 线程的例程函数
void *routine(void *arg)
{task *task_p; // 任务指针用来执行将要执行的任务thread_pool *pool (thread_pool*)arg;while(1){pthread_cleanup_push(pthread_cancel_handler, (void*)pool-task_list_lock);// 尝试持有互斥锁pthread_mutex_lock(pool-task_list_lock);// 判断是否有任务没有则进入睡眠// 1、没有任务线程池销毁开关断开则进入条件变量等待队列while(!pool-waiting_tasks !pool-shutdown){// 当条件不满足时会先自动解锁pool-lock然后等待到条件满足后会自动上锁pool-lockpthread_cond_wait(pool-task_list_cond, pool-task_list_lock);}// 2、没有任务线程池销毁开发闭合则先解锁然后退出if(!pool-waiting_tasks pool-shutdown){pthread_mutex_unlock(pool-task_list_lock); // 需要解锁pthread_exit(NULL);}// 3、有任务线程池销毁开关断开则取一个任务task_p pool-task_list-next;pool-task_list-next task_p-next; // 弹出第一任务节点 pool-waiting_tasks--; // 当前等待的任务数量-1,pthread_mutex_unlock(pool-task_list_lock); // 解锁// 弹出压入的线程取消函数运行到这里不执行但是当线程在前面被意外取消或中断会执行pthread_cleanup_pop(0); // 为了防止死锁执行任务期间不接受线程取消请求pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);(task_p-do_task)(task_p-arg); // 通过函数指针的方式执行任务// 执行完任务后接受线程取消请求pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);free(task_p); // 删除任务节点}pthread_exit(NULL);
}main.c线程池操作演示文件 // 线程池的测试案例#include stdio.h
#include unistd.h
#include thread_pool.hvoid *mytask(void *arg)
{ int n rand()%10;printf([%ld][%s] job will be done in %d sec...\n, pthread_self(), __FUNCTION__, n);sleep(n);printf([%ld][%s] job done!\n, pthread_self(), __FUNCTION__);
}void *func(void *arg )
{printf(this is a test by [%s]\n, (char*)arg);sleep(1);printf(test finish...\n);
}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、初始化线程池thread_pool *pool malloc(sizeof(thread_pool));init_pool(pool, 2);// 2、投放任务printf(throwing 3 tasks...\n);add_task(pool, mytask, NULL);add_task(pool, mytask, NULL);add_task(pool, mytask, NULL);// 3、查看当前线程池中的线程数量printf(current thread number: %d\n, total_thread(pool));sleep(9);// 4、再次投放任务printf(throwing another 2 tasks...\n);add_task(pool, mytask, NULL);add_task(pool, mytask, NULL);add_task(pool, func, (void *)Great Macro);// 5、添加2条线程printf(try add 2 threads, actual add %d\n, add_thread(pool, 2));printf(current thread number: %d\n, total_thread(pool));sleep(5);// 6、删除3条线程printf(try remove 3 threads, actual remove: %d\n, remove_thread(pool, 3));printf(current thread number: %d\n, total_thread(pool));sleep(5);// 7、 销毁线程池printf(destroy thread pool\n);destroy_pool(pool);return 0;
}注编译时把线程池文件和测试文件放在同一个工作目录下。 实际使用时只需要把thread_pool.h和thread_pool.c拷贝到自己的工程目录下然后根据规则操作线程池。
六、总结 线程池是许多线程的集合它不是线程组。线程池适用于任何需要处理大量任务或需要同时处理多个请求的应用程序的场景可以提供性能和效率。 至此Linux系统编程系列16篇完结撒花历时5天这年中秋国庆没有假放