LINUX免锁线程池C++
来源:互联网 发布:des算法 java 编辑:程序博客网 时间:2024/05/21 07:49
所有printf 都是调试信息。
工作原理:关键在Thread结构体设计,通过结合socketpair 把所有线程中socketpair的一端都加入select结合,使主线程可以知道子线程工作状态,同时主线程通过向fdMain写入一个字节,打破子线程read函数阻塞,开始处理任务;实现控制子线程;
全局的线程管理队列,用于主线程将任务分配给没有任务的消费者。
调试过程中问题:
001: 主文件描述符有事件直接从空闲队列中取一个,然后交换任务队列,再发信号也会造成逻辑混乱,不能再次触发select集合的事件;目前状态是标准输入有事件时唤醒一个线程,这次对fdMain有写操作,下一个循环才会起作用。
//write(thread->fdMain,"a",1); 这里多加了一个信号,执行完后线程进入等待队列,而文件描述符任然被监听,导致死循环;死循环状态应该可以正常处理任务,但是消耗资源,通过打印调试信息,发现的BUG,
002:list容器一不小心就搞错的地方,相当坑爹:it =working_queue.erase(it); //在遍历工作队列过程中擦除list中的节点会导致迭代器自动加1,必须与没有擦除的情况区分对待。因而不可以在for循环中进行it++,
改进办法:逆序遍历队列
#include<list> #include<stdio.h> #include<stdlib.h> #include<string.h> #include<pthread.h> #include<unistd.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> using namespace std; typedef struct myTask { char work[100]; }Task; //定义线程结构体,为每个消费者线程定义一个结构体与之对应 //参数1:用于与fdMain组成socketpair,消费者线程通过向fdThread中写数据给主线程发信号 //参数2:用于socket通信,触发select(fdMain会被加入到主线程中select集合中,当消费者即将进入休眠状态就往fdThread中写入一个字节,则该线程对应的fdMain就会触发 select事件;主线程将任务队列交换完成之后往该消费者线程对应的fdMain中写一个字节,导致消费者线程打破read函数阻塞,进入处理任务的循环) //参数3:任务队列 通过与生产者线程交换任务队列获取任务 typedef struct myThread { int fdThread; int fdMain; list<Task>* queue; }Thread; //定义全局线程管理队列,用于管理所有的消费者线程池 list<Thread*> working_queue; list<Thread*> waitting_queue; void*work_thread(void*argc) { Thread* thread = (Thread*)argc; char ch; int ret =0; while(1) { write(thread->fdThread,&ch,1); printf("waitting for task\n"); read(thread->fdThread,&ch,1); Task task; while(thread->queue->size()>0) { strncpy(task.work,(*(thread->queue->begin())).work,sizeof(task.work)); thread->queue->pop_front(); printf("subThread doing work now:%s\n",task.work); } //send signal of task complete printf("send signal of task complete\n"); } } int create_new_thread() { printf("wokao\n"); Thread* thread = new Thread; int fd[2]; // socketpair(int domain, int type, int protocol, int sv[2]); socketpair(AF_UNIX,SOCK_STREAM,0,fd); thread->fdThread = fd[0]; thread->fdMain = fd[1]; thread->queue = new list<Task>; working_queue.push_back(thread); pthread_t tid; pthread_create(&tid,NULL,work_thread,(void*)thread); return thread->fdMain; } //获取CPU个数,用于得到线程池中线程的个数 int num_of_CPU() { FILE* fp =popen("cat /proc/cpuinfo|grep processor|wc -l","r"); char buf[10]; fread(buf,1,sizeof(buf),fp); int num =atoi((const char*)buf); pclose(fp); return num; } int main() { //主线程(生产者线程)任务队列,用于从标准输入得到任务队列,由于只有主线程会修改这个队列,所以是安全的 list<Task>* task_queue = new list<Task>; int thread_num = num_of_CPU()*4; printf("thread_num :%d\n",thread_num); fd_set set; FD_ZERO(&set);int i=0; int fd_s[10]={0}; fd_s[0] = STDIN_FILENO; int fdMax=fd_s[0]; for(i=0;i<thread_num;i++) { printf("创建线程\n"); int t= create_new_thread(); printf("select监听的文件描述符:%d\n",t); if(t < 0) { printf("create_new_thread func err\n"); return -1; } printf("wokao\n"); fd_s[i+1] = t; if(fd_s[i+1]>fdMax) fdMax = fd_s[i+1]; } while(1) //int j=0;//for(j=0;j<15;j++){ printf("进入循环\n"); FD_SET(fd_s[0],&set); for(i=0;i< thread_num;i++) { FD_SET(fd_s[i+1],&set); } char ch; //select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout); int ret =select(fdMax+1,&set,NULL,NULL,NULL); if(ret>0) { printf("******************wokao********:%d\n",ret); // new input if(FD_ISSET(STDIN_FILENO,&set)) { printf("主线程有输入信号\n"); char buf[100]; fgets(buf,sizeof(buf),stdin); Task task; strncpy(task.work,buf,sizeof(buf)); task_queue->push_back(task); if(waitting_queue.size()>0) { Thread* thread =waitting_queue.front(); waitting_queue.pop_front(); working_queue.push_back(thread); write(thread->fdMain,&ch,1); /* list<Task>*tmp =new list<Task>; thread->queue = task_queue; task_queue = tmp; write(thread->fdMain,&ch,1); */ } } else { for(list<Thread*>::iterator it =working_queue.begin();it!=working_queue.end();) { Thread * thread = *it; if(FD_ISSET(thread->fdMain,&set)) { printf("子线程有输入信号,"); printf("有事件的文件描述符:%d\n",thread->fdMain); read(thread->fdMain,&ch,1); if(task_queue->size()>0) { printf("有事做加入执行任务队列\n"); list<Task>*tmp =new list<Task>; thread->queue = task_queue; task_queue = tmp; write(thread->fdMain,"a",1); waitting_queue.erase(it); working_queue.push_back(thread); //write(thread->fdMain,"a",1); 这里多加了一个信号,执行完后线程进入等待队列,而文件描述符任然被监听,导致死循环 } else { printf("没有事做加入等待队列\n"); it =working_queue.erase(it); //在遍历工作队列过程中擦除list中的节点会导致迭代器自动加1,必须与没有擦除的情况区分对待。因而不可以在for循环中进行it++ waitting_queue.push_back(thread); continue; } } it++; }} } } return 0; }
0 0
- LINUX免锁线程池C++
- linux c 线程锁
- linux c线程池
- Linux C线程池
- Linux C 线程池
- linux c 线程池
- Linux C 线程池
- Linux C 实现线程池
- linux c 线程池实例
- 简单Linux C线程池
- Linux c 简易线程池
- 简单Linux C线程池
- 简单Linux C线程池
- 简单Linux C线程池
- 简单Linux C线程池
- 简单Linux C线程池
- 简单Linux C线程池
- 简单Linux C线程池
- 九度 oj 题目1085:求root(N, k)
- 多态性——虚函数
- Python豆瓣爬虫
- Java编舟录一----简单介绍
- Mybatis工作机制源码分析—缓存机制及事务机制
- LINUX免锁线程池C++
- Realm数据库读取数据时的处理:每次加载数据时,都将原有的数据删除,重新读取模型中的数据
- org.codehaus.jackson.map包下的ObjectMapper类源码
- Java Thread(Android Nougat源码)
- 微信小程序入门之tabBar
- 学习笔记--mysql索引(四) 多列索引
- 递归小结
- iOS - NSPredicate
- 神注释