【Linux】半同步半异步线程池

 

首先来复习一下什么是:阻塞、非阻塞、同步、异步

阻塞:调用方在得到结果之前会一直等待;

非阻塞:调用方在不能得到结果时,不会等待。

打个比喻:David准备钓鱼回家买菜做饭。

阻塞:钓鱼时一直守在鱼竿前等鱼上钩,就这样钓到鱼后去做别的事情。

非阻塞:David抛出鱼竿后,没有鱼上钩,于是开始先买菜洗菜玩手机,鱼上钩后,再去处理鱼。

 

同步:被调用方得到最终结果之后才返回给调用方。所有的操作都做完,才返回给用户。这样用户在线等待的时间太长,给用户一种卡死了的感觉(就是系统迁移中,点击了迁移,界面就不动了,但是程序还在执行,卡死了的感觉)。这种情况下,用户不能关闭界面,如果关闭了,即迁移程序就中断了。

异步:不用等所有操作等做完,就相应调用方的请求。即先相应请求,然后慢慢去处理,调用方的体验比较好。

打个比喻:David叫nana去吃饭,而nana此刻正在化妆,收拾屋子,穿衣服......

同步:David叫了一声,没有得到任何回应,David听到后很可能去一个人走了;nana收到消息后没有回应,一直忙着自己的事情,化妆换衣服之后,给David发消息:我收好了,可以和你去吃饭。

异步:David叫了一声,nana说等等我有事情,David听到后就阻塞住;nana忙完后又告诉David:我收好了,可以和你去吃饭。

然后两个人手牵手去吃饭。

-------------------

这里同步的理解好像有些不恰当,

举例线程同步:

总之,同步讲的是被调用者的事情。 

-------------------

由此可见:异步是最优的解决方案,能够满足调用方和被调用方;同步可能让双方都达不到目的。

同步模式编程简单,但是I/O的利用率低;

而异步模式编程复杂,但是I/O利用率高。
综合同步异步的有优点,就有了半同步半异步的设计模式。

---------------------------------------------------------------------------------------------------------

半同步/半异步线程池中:添加一个共享队列(单例模式并保证线程安全)

【1】主线程只管理监听socket、连接,得到新的连接socket由工作线程来管理。当有新的连接到来时,主线程就接受并将新返回的连接socket派发给某个工作线程,此后,该连接套接字clifd上任何I/O操作都由被选中的工作线程来处理,知道客户端关闭连接。

【2】主线程向工作线程派发socket的方式,是通过一个共享队列,连接套接字clifd上有事件,主线程就将clifd添加到queue中。工作线程检测到queue上有数据,如果是,就处理该事件。(扩充:工作线程可以写成一个epoll事件处理模式)

【3】此模式每个线程都做自己的事件。

总结起来线程池就是: 
1. 同步服务层:不断的将新任务添加到同步队列中,可以用多路复用或者多线程来完成。一开始没看懂任务是什么,其实一个函数就是一个任务,C++11通过std::function将函数封装为类模板对象,可以将这些任务(函数)放到容器中保存起来,以进行添加读取任务操作。 
2. 排队层:就是一个同步队列,处于核心地位。所有待处理的任务都存在这里,要保证队列中共享数据线程安全(加锁),还要控制任务的数量,上层服务层往队列添加任务,下层从这里取任务去执行。 
3. 异步服务层: 预先创建好线程,来并行处理队列中的任务。


 

//项目中的代码分析:

共享队列:

//共享队列:单例,保证线程安全 队列为空,则阻塞等待。所以后面的锁需要重新new
class ShareQueue
{
public:
	static ShareQueue* getShareQueue();

	void Share_push(int clifd)
    {
	    _queue->push(clifd);
    }

	int Share_pop()
    {
	    Mylock::getMylock()->lock();
        while(_queue->empty()) 
	    {
		    sleep(1);
	    }
	    int clifd = _queue->front();
	    _queue->pop();
	    Mylock::getMylock()->unlock();
	    return clifd;
    }
private:
	ShareQueue()
    {
	    _queue = new queue<int>;
    }
	queue<int> *_queue; 
	static ShareQueue* _instance;//单例中的唯一实例对象

};

线程池:

class PthreadPool
{
public:

	PthreadPool(int n)//构造函数创建线程池
    {
	    for(int i = 0;i < n;i++)
	    {
	    	pthread_t _id;
		    pthread_create(&_id,NULL,Pth_run,NULL);
	    }
    }

	static void* Pth_run(void* arg)//线程函数:处理clifd上的事物
    {
	    while(1)
	    {
		    int clifd = ShareQueue::getShareQueue()->Share_pop();//工作线程阻塞在这里
		
		    if(clifd <= 0) continue;
		    string recv_str;//not tar json
		    int num = Server::getServer("127.0.0.1",6000)->SerRecv(recv_str);

		    if(num <= 0) //accept error
		    {	
		    	if(event_arr[clifd] != NULL)
			    {
			    	cout<<"NO."<<clifd<<" client accept over..."<<endl;
				    event_free(event_arr[clifd]);//delete the event 
				    event_arr[clifd] = NULL;
			    }
			    Server::getServer("127.0.0.1",6000)->SerCloseClifd();
		    }
		    else
		    {
		    	Contral::getContral()->Con_process(clifd,recv_str);
		    }
	    }
    }
};

主线程:libevent监听clifd 如果有事件就将clifd push到队列中

void System::Sys_recv(int clifd,short ev,void* arg)// only push clifd
{
	ShareQueue::getShareQueue()->Share_push(clifd);	//
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值