首先来复习一下什么是:阻塞、非阻塞、同步、异步
阻塞:调用方在得到结果之前会一直等待;
非阻塞:调用方在不能得到结果时,不会等待。
打个比喻: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); //
}