LINUX免锁线程池C++


所有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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值