结合考虑两种事件处理模式和几种I/O模型,半同步半异步存在多种变体,其中有一种变体是半同步半反应堆模式。
异步线程只有一个,由主线程来充当,它将监听套接字注册到epoll内核事件表中,如果监听套接字上有事件,主线程就接受它得到连接套接字,然后将连接套接字的事件也注册到epoll内核事件表中,如果连接套接字上有事件发生,就将该连接套接字插入到请求队列中,所有睡眠的工作线程通过竞争来获得任务的接管权,但是这个会出现惊群现象。
要想避免惊群问题,在仍然共用一个线程互斥锁的条件下,给每一个工作线程创建一个线程条件变量,主线程在添加任务时,找到空闲的工作线程,将任务置入该工作线程任务队列中,同时只通知 (pthread_cond_signal) 该工作线程的线程条件变量,工作线程与主线程虽然共用相同的线程互斥锁(因为有全局资源及调用 pthread_cond_wait 所需),但线程条件变量的通知过程却是定向通知的,未被通知的工作线程不会被唤醒,这样惊群现象也就不会产生了。当然,还有一些设计上的细节需要注意,比如:当没有空闲工作线程时,需要将任务添加进线程池的全局任务队列中,工作线程处理完自己的任务后需要查看一下全局任务队列中是否还有未处理的任务。
缺点:1.主线程和工作线程共享请求队列,所以他们对请求队列操作时,要进行加锁,白白耗费CPU时间。2.每个工作线程同一时间只能处理一个客户请求,如果客户请求过多,工作线程较少,请求队列中将堆积很多任务,客户端响应速度就变慢了,如果通过增加工作线程来解决,工作线程的切换也会耗费大量CPU时间。
相对高效的半同步半异步模式。让每个工作线程能同时处理多个客户连接。
主线程只管监听套接字,连接套接字由工作线程管理,当有新的连接到来时,主线程接收他并将返回的连接套接字分发给某个工作线程,此后连接套接字上的所有I/O操作都由工作线程处理,直到客户端关闭连接。