基于linux的多线程处理程序
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <assert.h> #include <stdbool.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/epoll.h> #include <fcntl.h> #include <pthread.h> #define DEBUG //调试信息 #ifndef DEBUG #define debug(...) #else //#ifdef DEBUG #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) #define debug(...) \ do \ { \ fprintf(stdout, "%s: %s:%d: ", __FILE__, __FUNCTION__, __LINE__); \ fprintf(stdout, __VA_ARGS__); \ putc('\n', stdout); \ } while (0) #else #define debug(args...) \ do \ { \ fprintf(stdout, "%s: %s:%d: ", __FILE__, __FUNCTION__, __LINE__); \ fprintf(stdout, ##args); \ putc('\n', stdout); \ } while (0) #endif #endif // DEBUG #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) #define error(...) \ do \ { \ fprintf(stderr, "%s: %s:%d: ", __FILE__, __FUNCTION__, __LINE__); \ fprintf(stderr, __VA_ARGS__); \ putc('\n', stderr); \ } while (0) #else #define error(args...) \ do \ { \ fprintf(stderr, "%s: %s:%d: ", __FILE__, __FUNCTION__, __LINE__); \ fprintf(stderr, ##args); \ putc('\n', stderr); \ } while (0) #endif #define WORKER_MULTIPLE_TASK_NUMBER 10 typedef struct worker_task_struct { void *(*process)(void *arg); void *arg; struct worker_task_struct *next; } worker_task_t; typedef struct threads_pool_struct { worker_task_t *pend_queue_head; /**< 任务结点链表,保存所有投递的任务 */ worker_task_t *free_queue_head; int max_task_num; int current_pend_task_num; worker_task_t *tasks_ptr; int shutdown; /**< 线程池销毁标志,1 -> 销毁 */ pthread_t *workers_id; pthread_mutex_t queue_lock; /**< 互斥锁 */ pthread_cond_t queue_ready; /**< 条件变量 */ int max_worker_num; int current_process_worker_num; void *(*func_routine)(void *arg); //常规函数(空闲) } threads_pool_t; static void *threads_pool_default_routine(void *arg) { assert(arg); threads_pool_t *base = (threads_pool_t *)arg; debug("ready to go [%x]", pthread_self()); while (1) { pthread_mutex_lock(&(base->queue_lock)); /**< 上锁, pthread_cond_wait()调用会解锁*/ while ((base->current_pend_task_num == 0) && (!base->shutdown)) /**< 队列没有等待任务*/ { debug("wait for [%x]", pthread_self()); pthread_cond_wait(&(base->queue_ready), &(base->queue_lock)); /**< 条件锁阻塞等待条件信号*/ } if (base->shutdown) { debug("shut down [0x%x]", pthread_self()); pthread_mutex_unlock(&(base->queue_lock)); pthread_exit(NULL); /**< 释放线程 */ debug("unable to this [%x]", pthread_self()); break; } base->current_process_worker_num++; worker_task_t *task = base->pend_queue_head; /**< 取等待队列任务结点头*/ base->pend_queue_head = task->next; /**< 链表后移 */ base->current_pend_task_num--; pthread_mutex_unlock(&(base->queue_lock)); (*(task->process))(task->arg); /**< 执行回调函数 */ pthread_mutex_lock(&(base->queue_lock)); task->next = base->free_queue_head; base->free_queue_head = task; base->current_process_worker_num--; pthread_mutex_unlock(&(base->queue_lock)); } return 0; } int threads_pool_init(threads_pool_t *base, int max_thread_num) { assert(!base->workers_id); assert(!base->pend_queue_head); assert(!base->free_queue_head); base->shutdown = 0; pthread_mutex_init(&(base->queue_lock), NULL); /**< 初始化互斥锁 */ pthread_cond_init(&(base->queue_ready), NULL); /**< 初始化条件变量 */ /**< 创建任务 */ int task_num = max_thread_num * WORKER_MULTIPLE_TASK_NUMBER; worker_task_t *tasks = (worker_task_t *)malloc(sizeof(worker_task_t) * task_num); for (int j = 0; j < (task_num - 1); j++) { tasks[j].next = &(tasks[j + 1]); } tasks[task_num - 1].next = NULL; base->free_queue_head = base->tasks_ptr = tasks; base->max_task_num = task_num; debug("max task num is %d", task_num); base->workers_id = (pthread_t *)malloc(sizeof(pthread_t) * max_thread_num); base->func_routine = threads_pool_default_routine; base->current_process_worker_num = 0; for (int i = 0; i < max_thread_num; i++) { /**< 创建线程 */ if (!pthread_create(&(base->workers_id[i]), NULL, base->func_routine, (void *)base)) { debug("create thread id is %d:[%x]", i, base->workers_id[i]); base->max_worker_num++; /**< 当前池中的线程数 */ } else { error("create worker failed"); break; } usleep(1000); } return 0; } int threads_pool_destroy(threads_pool_t *base) { debug("destroy"); if (base->shutdown) /**< 已销毁 */ { error("already close!"); return -1; } base->shutdown = 1; /**< 销毁标志置位 */ pthread_cond_broadcast(&(base->queue_ready)); /**< 唤醒所有pthread_cond_wait()等待线程 */ debug("broadcast workers to shut down"); for (int i = 0; i < base->max_worker_num; i++) { pthread_join(base->workers_id[i], NULL); /**< 等待所有线程执行结束 */ debug("destroy worker id is %d:[%x]", i, base->workers_id[i]); } free(base->workers_id); /**< 释放 */ debug("free workers"); free(base->tasks_ptr); /**< 释放 */ debug("free tasks"); pthread_mutex_destroy(&(base->queue_lock)); /**< 销毁 */ pthread_cond_destroy(&(base->queue_ready)); /**< 销毁 */ return 0; } int threads_pool_task_status(threads_pool_t *base, int *pend_task_num, int *process_task_num) { assert(base); pthread_mutex_lock(&(base->queue_lock)); if (pend_task_num) *pend_task_num = base->current_pend_task_num; debug("current pend task num is %d", base->current_pend_task_num); if (process_task_num) *process_task_num = base->current_process_worker_num; debug("current process task num is %d", base->current_process_worker_num); pthread_mutex_unlock(&(base->queue_lock)); return 0; } int threads_pool_submit_task(threads_pool_t *base, void *(*process)(void *arg), void *arg) { assert(base); assert(process); worker_task_t *task; do { pthread_mutex_lock(&(base->queue_lock)); task = base->free_queue_head; if (task) { base->free_queue_head = task->next; task->process = process; task->arg = arg; task->next = base->pend_queue_head; base->pend_queue_head = task; base->current_pend_task_num++; debug("current pend task num is %d", base->current_pend_task_num); } else { error("not free task to use"); } pthread_mutex_unlock(&(base->queue_lock)); } while (!task && !usleep(1000)); /**< 等待有空闲任务 */ pthread_cond_signal(&(base->queue_ready)); /**< 发送信号给1个处于条件阻塞等待状态的线程 */ return 0; }
测试代码:
void *handle_say_hello(void *arg) { printf("hello world\n"); return NULL; } int main(int argc, char *argv[]) { // 创建处理连接的线程池 threads_pool_t tpool; memset(&tpool, 0, sizeof(threads_pool_t)); threads_pool_init(&tpool, 3); for (size_t i = 0; i < 50; i++) { threads_pool_submit_task(&tpool, handle_say_hello, NULL); } int pend_num, process_num; threads_pool_task_status(&tpool, &pend_num, &process_num); usleep(1000); threads_pool_task_status(&tpool, &pend_num, &process_num); threads_pool_destroy(&tpool); return 0; }
测试结果:
ras_server.c: threads_pool_init:163: max task num is 30 ras_server.c: threads_pool_init:174: create thread id is 0:[94d0a700] ras_server.c: threads_pool_default_routine:102: ready to go [94d0a700] ras_server.c: threads_pool_default_routine:110: wait for [94d0a700] ras_server.c: threads_pool_init:174: create thread id is 1:[94509700] ras_server.c: threads_pool_default_routine:102: ready to go [94509700] ras_server.c: threads_pool_default_routine:110: wait for [94509700] ras_server.c: threads_pool_init:174: create thread id is 2:[93d08700] ras_server.c: threads_pool_default_routine:102: ready to go [93d08700] ras_server.c: threads_pool_default_routine:110: wait for [93d08700] ras_server.c: threads_pool_submit_task:253: current pend task num is 1 ras_server.c: threads_pool_submit_task:253: current pend task num is 2 ras_server.c: threads_pool_submit_task:253: current pend task num is 3 ras_server.c: threads_pool_submit_task:253: current pend task num is 4 ras_server.c: threads_pool_submit_task:253: current pend task num is 5 ras_server.c: threads_pool_submit_task:253: current pend task num is 6 ras_server.c: threads_pool_submit_task:253: current pend task num is 7 ras_server.c: threads_pool_submit_task:253: current pend task num is 8 ras_server.c: threads_pool_submit_task:253: current pend task num is 9 ras_server.c: threads_pool_submit_task:253: current pend task num is 10 ras_server.c: threads_pool_submit_task:253: current pend task num is 11 ras_server.c: threads_pool_submit_task:253: current pend task num is 12 ras_server.c: threads_pool_submit_task:253: current pend task num is 13 ras_server.c: threads_pool_submit_task:253: current pend task num is 14 ras_server.c: threads_pool_submit_task:253: current pend task num is 15 ras_server.c: threads_pool_submit_task:253: current pend task num is 16 ras_server.c: threads_pool_submit_task:253: current pend task num is 17 ras_server.c: threads_pool_submit_task:253: current pend task num is 18 ras_server.c: threads_pool_submit_task:253: current pend task num is 19 ras_server.c: threads_pool_submit_task:253: current pend task num is 20 ras_server.c: threads_pool_submit_task:253: current pend task num is 21 ras_server.c: threads_pool_submit_task:253: current pend task num is 22 ras_server.c: threads_pool_submit_task:253: current pend task num is 23 ras_server.c: threads_pool_submit_task:253: current pend task num is 24 ras_server.c: threads_pool_submit_task:253: current pend task num is 25 ras_server.c: threads_pool_submit_task:253: current pend task num is 26 ras_server.c: threads_pool_submit_task:253: current pend task num is 27 ras_server.c: threads_pool_submit_task:253: current pend task num is 28 ras_server.c: threads_pool_submit_task:253: current pend task num is 29 ras_server.c: threads_pool_submit_task:253: current pend task num is 30 ras_server.c: threads_pool_submit_task:257: not free task to use hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world ras_server.c: threads_pool_default_routine:110: wait for [94d0a700] ras_server.c: threads_pool_default_routine:110: wait for [93d08700] ras_server.c: threads_pool_default_routine:110: wait for [94509700] ras_server.c: threads_pool_submit_task:253: current pend task num is 1 ras_server.c: threads_pool_submit_task:253: current pend task num is 2 ras_server.c: threads_pool_submit_task:253: current pend task num is 3 ras_server.c: threads_pool_submit_task:253: current pend task num is 4 ras_server.c: threads_pool_submit_task:253: current pend task num is 5 ras_server.c: threads_pool_submit_task:253: current pend task num is 6 ras_server.c: threads_pool_submit_task:253: current pend task num is 7 ras_server.c: threads_pool_submit_task:253: current pend task num is 8 ras_server.c: threads_pool_submit_task:253: current pend task num is 9 ras_server.c: threads_pool_submit_task:253: current pend task num is 10 ras_server.c: threads_pool_submit_task:253: current pend task num is 11 ras_server.c: threads_pool_submit_task:253: current pend task num is 12 ras_server.c: threads_pool_submit_task:253: current pend task num is 13 ras_server.c: threads_pool_submit_task:253: current pend task num is 14 ras_server.c: threads_pool_submit_task:253: current pend task num is 15 ras_server.c: threads_pool_submit_task:253: current pend task num is 16 ras_server.c: threads_pool_submit_task:253: current pend task num is 17 ras_server.c: threads_pool_submit_task:253: current pend task num is 18 ras_server.c: threads_pool_submit_task:253: current pend task num is 19 ras_server.c: threads_pool_submit_task:253: current pend task num is 20 ras_server.c: threads_pool_task_status:226: current pend task num is 20 ras_server.c: threads_pool_task_status:229: current process task num is 0 hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world ras_server.c: threads_pool_default_routine:110: wait for [93d08700] hello world ras_server.c: threads_pool_default_routine:110: wait for [94d0a700] hello world ras_server.c: threads_pool_default_routine:110: wait for [94509700] ras_server.c: threads_pool_task_status:226: current pend task num is 0 ras_server.c: threads_pool_task_status:229: current process task num is 0 ras_server.c: threads_pool_destroy:190: destroy ras_server.c: threads_pool_destroy:200: broadcast workers to shut down ras_server.c: threads_pool_default_routine:116: shut down [0x93d08700] ras_server.c: threads_pool_default_routine:116: shut down [0x94d0a700] ras_server.c: threads_pool_default_routine:116: shut down [0x94509700]ras_server.c: threads_pool_destroy:205: destroy worker id is 0:[94d0a700] ras_server.c: threads_pool_destroy:205: destroy worker id is 1:[94509700] ras_server.c: threads_pool_destroy:205: destroy worker id is 2:[93d08700] ras_server.c: threads_pool_destroy:208: free workers ras_server.c: threads_pool_destroy:211: free tasks
关键要点:
1、在启动前,预先创建好固定数目的工作线程,而不是动态创建,因为调用pthread_exit()之后,实际上系统并没有真正回收线程资源,而是必须调用pthread_join()才可以,所以会造成内存泄漏。
2、在启动前,预先创建好固定数目的任务书结构,并且循环利用,减少动态创建的时间消耗。
3、在提交任务书的时候,假如没有空闲的,需要等待1毫秒。任务书的数目与等待时长,往后可以根据实际应用做调整,并不会造成太大影响。
4、在任务书的执行顺序上,没有使用FIFO的方式,而是后进先出的方式。可能会造成一定程度困惑。主要是考虑队列的长度太长,添加到尾部,需要遍历所有,造成与任务书数目成正比的耗时。
5、目前代码中,预留任务书的数目是工作线程数目的10倍,可根据应用场合调整该值。