linux 下实现简单的异步多线程任务分发
来源:互联网 发布:淘宝套现容易被发现吗 编辑:程序博客网 时间:2024/05/29 04:57
linux 下实现简单的异步多线程任务分发
异步多任务并行处理在linux场景下有很多种实现方式。
当前主流有几种方案:多进程方案,1 master+N workers 方式,类似apache等等;多线程方案,或维护一个线程池。
本例子实现一个简单的单进程多线程异步分发任务模型。本实现有如下特点:
* 维护线程简单
* 直接可以传送函数,函数的参数
* 线程之间使用消息队列通信,实时性高,简单
* 可以实现简单的与主线程分离的任务
* swoole中,nodejs中的异步任务,需要在特定的io或事件场合,才可以异步,并不是随时可以异步,本例子实现随时任何情形都可以异步
受限于消息队列等因素,本例子还有未实现功能:
* 线程间没有锁机制,共享数据将不安全
* linux消息队列每个元素最大只能65535字节
* 暂时不能实现协程调度,工作线程和主线程是异步的,但是1个工作线程同时只能接受到1个任务,必须执行完才能接下一个任务。如果总共三个工作线程,每一个都阻塞,那就不能继续执行新的任务。
后续更新:
* 使用getcontext,setcontext,swapcontext来实现协程调度
* 实现有锁调用和无锁调用两种方式
code.c:
/* * 实现简单消息队列分发任务异步执行 * c99 thread.c -o thread -l pthread -D _XOPEN_SOURCE * */#define _XOPEN_SOURCE 1#include <pthread.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <time.h>#include <sys/ipc.h>#include <sys/msg.h>#include <stdarg.h>#include <string.h>#define LOGF(STR, ...) logfmt(STR, __FILE__,__LINE__,__func__,##__VA_ARGS__)int msgqid = 0; // 用于主线程和worker线程通信的消息队列id,由主线程申请int msgtype = 9812; // 消息标识符,类型int IPC_KEY = 100011; // 申请消息的IPC_KEY 本应该用ftok获得/* 消息队列消息结构 *//* 以下消息队列中保存的指针,只在当前运行进程内有效,其他进程或者重启进程将失效 */typedef struct message { long msg_type; /* 消息标识符 */ void* (*func)(void*); /* 调用函数指针 在不同进程和进程重启后将失效 切记 */ void* param; /* 此函数的参数 */ void** retval; /* 此函数的返回值接收地址 发送方提供 发送方先申请好空间 */ void* (*callback)(void* retval, void*origin_param); /* 完成后的callback函数 参数依次是 返回值,上次调用的函数的参数 */} message;/* 线程任务单元 */typedef struct threadtask { pthread_t tid; int is_working; int working_times;} threadtask;/* 小线程池 */typedef struct taskpool { threadtask* pool; int len;} taskpool;taskpool* mainpool;void logfmt(const char* str, const char* file, const int line, const char* func, ...);/*protected 线程的main函数 task consumer */void* taskconsumer(void*ptr) { threadtask* myth = (threadtask*)ptr; struct message msgq; int i = 0; for (i=0; ;i++) { msgrcv(msgqid, &msgq, sizeof(message)-sizeof(long), msgtype, 0); myth->is_working = 1; myth->working_times++; LOGF("threadtask(%ld) got a task: loopid=%d taskfunc=%p param=%d\n", myth->tid, i, msgq.func, (msgq.param==NULL?0:*(int*)msgq.param)); if (msgq.func == NULL) { LOGF("msgq.func is NULL, ignore this msg\n"); continue; } void*ret = msgq.func((void*)msgq.param); if (msgq.retval != NULL) { *(msgq.retval) = ret; } myth->is_working = 0; if (msgq.callback != NULL) { LOGF("this task has callback loopid=%d\n", i); msgq.callback((void*)ret, msgq.param); } }}/*public user task*/void* task1(void* ptr) { LOGF("task1 start, void*=%p v=%d\n", ptr, *(int*)ptr); sleep(1); LOGF("task1 over\n"); return NULL;}void* task2(void* ptr) { LOGF("task2 start, void*=%x v=%d\n", ptr, *(int*)ptr); sleep(10); LOGF("task2 over\n");}void* callback1(void* ret, void* param) { LOGF("i am callback: ret=%p param=%p\n", ret, param);}int recycletaskpool(taskpool* thepool);/*for main 分配线程池 init workers, waiting msgqueue for task*/taskpool* inittaskpool(int worker_num) { taskpool* mainpool = (taskpool*)malloc(sizeof(taskpool)); memset(mainpool, 0, sizeof(*mainpool)); mainpool->pool = (threadtask*)malloc(sizeof(threadtask)*worker_num); memset(mainpool->pool, 0, sizeof(*mainpool->pool)); mainpool->len = worker_num; int has_error = 0; int i = 0; for (i=0; i<worker_num; ++i) { pthread_t * tid_ptr = &(mainpool->pool[i].tid); int ret = pthread_create(tid_ptr, NULL, (void *)taskconsumer, (void*)&(mainpool->pool[i])); if (ret != 0) { perror("create thread failed:"); has_error = 1; break; } pthread_detach(*tid_ptr); } if (has_error) { LOGF("create pthread error, go to recyclepool\n"); recycletaskpool(mainpool); } return mainpool;}/*for main 回收线程池资源*/int recycletaskpool(taskpool* thepool) { if (thepool != NULL) { if (thepool->pool != NULL) { free(thepool->pool); } free(thepool); } return 0;}/*public 异步启动一个任务*/int async_do(void*(*func)(void*), void* param, void** ret, void* (*callback)(void*,void*)) { struct message msgq; msgq.msg_type = msgtype; msgq.func = func; msgq.param = param; msgq.retval = ret; msgq.callback = callback; // send msgq to queue int sendret = msgsnd(msgqid, &msgq, sizeof(message)-sizeof(long), 0); return sendret;}int main() { // 初始化队列 msgqid = msgget(IPC_KEY, IPC_CREAT | 0666); // 初始化4个工作线程 int worker_num = 4; mainpool = inittaskpool(worker_num); char s[100] = {}; int sendret = 0; void* (*taskfunc) (void*) = NULL; void* (*thecallback) (void*,void*) = NULL; int dorecycle = 0; int i = 0; for (i=0; i<16; i++) { LOGF("input which task run: i=%d\n", i); scanf("%s", s); switch (s[0]) { case '1': taskfunc = task1; thecallback = NULL; break; case '2': taskfunc = task2; thecallback = NULL; break; case '3':case '4':case '5':case '6':case '7':case '8': taskfunc = task2; thecallback = callback1; break; case 'q': dorecycle = 1; // 退出 break; default: continue; break; } if (dorecycle) { break; } sendret = async_do(taskfunc, &s[0], NULL, thecallback); //LOGF("you put v=%d when i=%d ret=%d\n", v, i, sendret); } // do recycle int rmmsgret = msgctl(msgqid, IPC_RMID, NULL); LOGF("msgqid %d have removed: ret=%d\n", msgqid, rmmsgret); recycletaskpool(mainpool); LOGF("mainpool has been recycled\n");}// logvoid logfmt(const char* str, const char* file, const int line, const char* func, ...) { time_t now = time(NULL); struct tm* tn = localtime(&now); static char prestr[256] = {}; // 先预备好前缀 sprintf(prestr, "[%04d-%02d-%02d %02d:%02d:%02d] [%s:%d %s()] %s\x00", tn->tm_year+1900,tn->tm_mon+1,tn->tm_mday,tn->tm_hour,tn->tm_min,tn->tm_sec, file, line, func, str); va_list ap; va_start(ap, func); // 不能使用printf 要使用vprintf vprintf(prestr, ap); va_end(ap);}
运行结果:
[root@iZ25gcs79rvZ thread]# ./thread [2017-07-13 16:54:44] [thread.c:164 main()] input which task run: i=01[2017-07-13 16:54:46] [thread.c:164 main()] input which task run: i=1[2017-07-13 16:54:46] [thread.c:57 taskconsumer()] threadtask(139760144594688) got a task: loopid=0 taskfunc=0x400bc9 param=49[2017-07-13 16:54:46] [thread.c:78 task1()] task1 start, void*=0x7fff4f3fb0b0 v=492[2017-07-13 16:54:46] [thread.c:164 main()] input which task run: i=2[2017-07-13 16:54:46] [thread.c:57 taskconsumer()] threadtask(139760152987392) got a task: loopid=0 taskfunc=0x400c32 param=50[2017-07-13 16:54:46] [thread.c:85 task2()] task2 start, void*=4f3fb0b0 v=502[2017-07-13 16:54:47] [thread.c:164 main()] input which task run: i=3[2017-07-13 16:54:47] [thread.c:57 taskconsumer()] threadtask(139760161380096) got a task: loopid=0 taskfunc=0x400c32 param=50[2017-07-13 16:54:47] [thread.c:85 task2()] task2 start, void*=4f3fb0b0 v=502[2017-07-13 16:54:47] [thread.c:80 task1()] task1 over[2017-07-13 16:54:47] [thread.c:164 main()] input which task run: i=4[2017-07-13 16:54:47] [thread.c:57 taskconsumer()] threadtask(139760169772800) got a task: loopid=0 taskfunc=0x400c32 param=50[2017-07-13 16:54:47] [thread.c:85 task2()] task2 start, void*=4f3fb0b0 v=502[2017-07-13 16:54:48] [thread.c:164 main()] input which task run: i=5[2017-07-13 16:54:48] [thread.c:57 taskconsumer()] threadtask(139760144594688) got a task: loopid=1 taskfunc=0x400c32 param=50[2017-07-13 16:54:48] [thread.c:85 task2()] task2 start, void*=4f3fb0b0 v=502[2017-07-13 16:54:48] [thread.c:164 main()] input which task run: i=6[2017-07-13 16:54:56] [thread.c:87 task2()] task2 over[2017-07-13 16:54:56] [thread.c:57 taskconsumer()] threadtask(139760152987392) got a task: loopid=1 taskfunc=0x400c32 param=50[2017-07-13 16:54:56] [thread.c:85 task2()] task2 start, void*=4f3fb0b0 v=50[2017-07-13 16:54:57] [thread.c:87 task2()] task2 over[2017-07-13 16:54:57] [thread.c:87 task2()] task2 over[2017-07-13 16:54:58] [thread.c:87 task2()] task2 overq[2017-07-13 16:55:04] [thread.c:196 main()] msgqid 917504 have removed: ret=0[2017-07-13 16:55:04] [thread.c:198 main()] mainpool has been recycled
阅读全文
0 0
- linux 下实现简单的异步多线程任务分发
- java 多线程实现任务分发
- java 多线程实现任务分发
- linux下内核多线程的简单实现
- linux 下c++多线程的简单实现
- 用异步任务实现简单的新闻资讯
- spring+ActiveMQ+JMS+线程池实现简单的分布式,多线程,多任务的异步任务处理系统
- spring+ActiveMQ+JMS+线程池实现简单的分布式,多线程,多任务的异步任务处理系统
- ActiveMQ(六):spring+ActiveMQ+线程池实现简单的分布式,多线程,多任务的异步任务处理系统
- 使用Java多线程实现任务分发
- 使用Java多线程实现任务分发
- 使用Java多线程实现任务分发
- 使用Java多线程实现任务分发
- 使用Java多线程实现任务分发
- 嵌入式Linux 下用户程序实现多任务的方法:多线程的实现
- Linux 简单的多线程实现
- Android编程基础之异步任务的简单实现
- linux下异步IO的简单例子
- eclipse 安装问题
- 如何在BIGEMAP中打开excel、txt、dat文件
- 自动折叠式楼层
- python学习系列---list,tuple,dict,set内置方法
- Android px与dp转换
- linux 下实现简单的异步多线程任务分发
- 蚂蚁金服CTO程立:技术的价值与意义_在我看来是这样的
- 用qextserialport-1.2rc库写一个串口小工具
- Cirf10_quick 与 Cirf10_full 区别
- 牛腩新闻发布系统——初探JQuery,AJAX
- iOS开发之来自一线开发者的Swift学习资源推荐
- 最小公倍数
- HDU 2896 病毒侵袭
- 第三章 最小二乘问题