c语言进程池的简单实现
来源:互联网 发布:新东方网络视频哪里买 编辑:程序博客网 时间:2024/04/26 06:51
一、背景
上期文章提了一下线程池的简单实现,本期顺势也把进程池的学习过程给大家分享一下吧;
二、相关知识
2.1 进程池的使用场景
进程池与线程池出发点一样,都是考虑多核情况下任务的并行处理;
从多进程和多线程编程的区别上看,多线程有许多的同步、互斥的方法,较擅长于异步协作;而多进程同步、互斥的方法相对比较麻烦,则更多地考虑上下文独立执行;
从Nginx使用线程池/进程池处理大并发的思路去分析,其实就是多客户端大量连接的场景;主进程监听是否有新客户端tcp连接,然后分发给工作进程去响应http请求,在这种场景下每个连接都是一个独立的上下文逻辑,每个工作进程的内容都是对等地处理http请求,这种情况就非常适合进程池的方式;
2.2 进程池的工作流程
把上述的场景给抽象出来,关注于父子进程的通讯,也就是下图流程:
Master主进程给Worker子进程分发任务,通讯的方式用的就是单向管道的方式;
分发任务这块,可以有随机分发、轮流分发、计分板等多种方法,具体可以结合业务进行设计;
三、实现原型
本节根据上述流程图进行一个简单的实现,假设分别有A、B、C三种任务,主进程使用轮流分发(Round-robin)把任务均匀地分配给子进程们;
在结构体process上得考虑一下,Master进程需要保存所有子进程的进程号、管道号;
/* Process struct */typedef struct process{ char name[SIZE_NAME_NORMAL]; /* Process name */ pid_t pid; /* Process id */ int pipefd[2]; /* Connection between master/slave */ size_t score; /* Score record */} process_t;/* Program instance */typedef struct instance{ char prg_name[SIZE_NAME_LONG]; /* Program name with path */ char cfg_name[SIZE_NAME_LONG]; /* Configure name with path */ u16 process_num; /* Sub process number */ u16 process_idx; /* Current process index */ struct process *proc; /* Process struct */} instance_t;
int process_pool(instance_t *pinst, u16 process_num){ int ret = FAILURE; int ix = 0; int status = 0; if ( !pinst || !process_num ) { printf("NULL\n"); goto _E1; } signal(SIGINT, __sig_quit); signal(SIGTERM, __sig_quit); pinst->process_idx = 0; pinst->process_num = process_num; pinst->proc = (process_t *)calloc(process_num + 1, sizeof(process_t)); if ( !pinst->proc ) { printf("Alloc process pool struct failed\n"); goto _E1; } for ( ix = 1; ix <= process_num; ix++ ) { int bufsize = 1; ret = pipe(pinst->proc[ix].pipefd); if ( SUCCESS != ret ) { printf("socketpair\n"); goto _E2; } printf("Setup worker#%u\n", ix); pinst->proc[ix].pid = fork(); if ( pinst->proc[ix].pid < 0 ) { printf("fork\n"); goto _E2; } else if ( pinst->proc[ix].pid > 0 ) { /* Father */ CLOSE_FD(pinst->proc[ix].pipefd[0]); continue; } else { /* Child */ CLOSE_FD(pinst->proc[ix].pipefd[1]); pinst->process_idx = ix; ret = __worker(pinst); goto _E2; } } ret = __master(pinst); /* Waiting workers */ for ( ix = 1; ix <= pinst->process_num; ix++ ) { waitpid(pinst->proc[ix].pid, &status, WNOHANG); }_E2: for ( ix = 1; ix <= pinst->process_num; ix++ ) { CLOSE_FD(pinst->proc[ix].pipefd[1]); CLOSE_FD(pinst->proc[ix].pipefd[0]); } FREE_POINTER(pinst->proc);_E1: return ret;}
然后就是Master Worker的实现,Master就是简单地进行一个事件分发,通过约定的协议"ABC"代表三种工作命令,“Q“代表退出进程;
static int __master(instance_t *pinst){ int ret = 0; int fd = 0; int ix = 0; int roll = 0; char c = 0;#define __offset(pinst) ((pinst)->proc[(pinst)->process_idx])#define __round_robin(pinst, roll) \ ((pinst)->proc[((roll) % (pinst)->process_num) + 1].pipefd[1]) printf("Master#%u setup\n", pinst->process_idx); for ( g_enable = 1; g_enable; ) { /* Get pipe fd by round-robin */ fd = __round_robin(pinst, ++roll); c = 'A' + roll % 3; // 'A'/'B'/'C' ret = write(fd, &c, 1); if ( ret <= 0 ) { return FAILURE; } sleep(1); } /* Tell all workers to quit */ for ( ix = 1; ix <= pinst->process_num; ix++ ) { c = 'Q'; write(__round_robin(pinst, ++roll), &c, 1); } printf("Master#%u shutdown\n", pinst->process_idx); return SUCCESS;}
static int __worker(instance_t *pinst){ int fd = __offset(pinst).pipefd[0]; int ix = 0; ssize_t read_byte = FAILURE; char buffer[1024] = {0}; printf("Worker#%u setup\n", pinst->process_idx); for ( g_enable = 1; g_enable; ) { read_byte = read(fd, buffer, sizeof(buffer)); if ( read_byte <= 0 ) { if ( errno == EAGAIN || errno == EINTR ) { continue; } return FAILURE; } for ( ix = 0; ix < read_byte; ix++ ) { switch ( buffer[ix] ) { case 'A': case 'B': case 'C': __offset(pinst).score += buffer[ix]; printf("Worker#%u Recv command: %c, score: %llu\n", pinst->process_idx, buffer[ix], __offset(pinst).score); break; case 'Q': printf("Quit\n"); g_enable = 0; break; default: break; } } } printf("Worker#%u shutdown\n", pinst->process_idx); return SUCCESS;}最后带一个main方法,考虑了进程释放的问题,在这个demo中使用信号对genable进行控制;
static u8 g_enable; /* Running flag */static void __sig_quit(int sig){ g_enable = 0;}int main(int argc, char *argv[]){ instance_t inst = {0}; if ( argc < 2 ) { printf("Usage: \n\t%s < process number >\n", argv[0]); return EXIT_FAILURE; } return process_pool(&inst, atoi(argv[1]));}
四、总结
在本次demo原型中,进程池的开启方法process_pool()是比较通用的,管道的方法除了pipe也可以用socketpair;在功能扩展方面,要考虑的还有协议的完善、子进程执行结果是否需要告知主进程、应用socket的I/O复用、完善信号的处理等;
参考文章:
[1] http://blog.csdn.net/al_xin/article/details/39258067
[2] http://blog.csdn.net/u010693037/article/details/51335038
0 0
- c语言进程池的简单实现
- C语言实现简单的守护进程及信号处理
- C语言枚举进程,实现一个简单的内存补丁
- C语言简单进程
- c语言线程池的简单实现
- C语言实现简单的线程池
- 【C语言】C语言实现简单的链表
- C语言实现一个简单的线程池
- 一个简单线程池的实现 --C语言
- 一个简单的HashMap C语言实现
- c语言实现的简单二叉树
- 一个简单的HashMap C语言实现
- C语言,简单栈的实现 Stack
- BloomFilter的一个简单实现(C语言)
- C语言实现一个简单的计算器
- 泛型栈-C语言的简单实现
- C语言的HashTable简单实现
- 简单的C语言实现程序
- 秒杀系统架构
- AVR单片机笔记 mega16 PC口2345管脚
- MQTT---HiveMQ源码详解(十六)TopicTree
- CPU简史
- 记面试中知识疑惑点
- c语言进程池的简单实现
- Android组件----Activity
- 数据库跨越访问
- k-邻近算法学习
- tensorflow function笔记: dropout
- NYOJ-71 独木舟上的旅行(贪心)
- Java EE知识储备(五)
- linux几个常用命令总结
- Python_01