网络编程问题总结(六)
线程池服务模型是single thread 与 request per thread两种模型的折中方案,其在实现时通常需要借助任务队列,主线程往任务队列尾添加任务,线程池中的服务线程不断从任务队列头取任务并服务,如下图所示:
对于主线程和服务线程来说,任务队列是临界资源,需要加锁进行保护。主线程往任务队列添加任务时需要加锁,服务线程从任务队列取任务也许加锁,当服务线程发现任务队列空时需等待(主线程往任务队列中加任务时唤醒等待的服务线程),同时当队列满时,主线程需等待,当服务线程取走一个任务时唤醒主线程,为简化问题,假设任务队列是无限大的即主线程不需要等待。
主线程:
- pthread_mutex_lock(&lock);
-
queue.push(task);
-
pthread_mutex_unlock(&lock);
- pthread_cond_signal(&cond);
服务线程:
- while(true) {
- pthread_mutex_lock(&lock);
-
while(queue.empty() == true) {
- pthread_cond_wait(&cond, &lock);
-
}
- task = queue.pop();
- pthread_mutex_unlock(&lock);
-
server(task);
- }
上面的代码片段中,最难理解的应该红色字体部分了,为什么要用while循环,而不是if,这是因为pthread_cond_signal存在假唤醒(如信号等的影响)的情形,pthread_cond_signal的man手册中是这么描述的:
The pthread_cond_signal()
function shall unblock at least one of
the threads that are blocked on the specified condition
variable cond (if any threads are blocked on cond).
对于线程池的模型,假设某一时任务队列为空,这时所有的服务线程调用pthread_cond_wait,接着主线程往任务队列中添加一个任务,并调用pthread_cond_signal,这是可能有多个服务线程(unblock at least one)被唤醒,假设有两个服务线程被唤醒,如果使用if,这时两个线程都会调用pop,其中一个必然会失败,如果queue实现得不够好,可能引发大的错误,stl的queue为空时,调用front、back的行为是未定义的。