前面我们介绍了半同步半异步线程池的概念,下面我们首先说一下同步队列。
同步队列是半同步半异步线程池三层中的中间层——排队层。它一方面提供接口给上面同步服务层添加新任务,一方面提供接口给下面的异步服务层获取任务。上层可能是并发的添加任务,因此同步队列需要保证任务是线程安全的,同时它还需要确保任务队列的长度不会超过上限,避免内存消耗过大。
功能
下面我们需要详细分析同步队列的功能,它需要的成员和接口,这样代码就可以自然而然的设计出来。
首先我们考虑它需要的成员变量。
队列
需要有一个队列来保存任务
同步
需要有一个锁,来确保从队列中push和pop线程安全,不光如此,和队列相关的其他操作也需要锁来保证安全。
条件变量
为什么需要条件变量?考虑一个问题,当下面的异步服务层从同步队列获取任务时,同步队列中没有任务,对应的线程将会等待。那什么时候唤醒呢?就是当同步服务层添加新的任务的时候,此时队列非空,那么就可以唤醒之前的线程,取出任务去执行。因此需要一个条件变量在队列非空的时候发出通知。
同时,我们上面提到,同步队列需要确保队列长度不会过长,因此当队列已经达到上限时,需要限制同步层添加任务,让它等待,此时,当异步服务层取走任务队列未满的时候,就要将它唤醒,因此也要一个条件变量在队列未满的时候发出通知。
接口考虑它需要的接口。
添加任务
需要提供接口给同步服务层添加任务进队列。在添加任务的时候,需要上锁,确保过程是线程安全的。同时需要判断现在队列中任务数量是否到达上限,如果达到上限,需要等待队列未满条件变量发出通知。
取出任务
需要提供接口给异步服务层取出任务。同样,在取任务的时候,需要上锁,确保过程是线程安全的。同时需要判断现在队列是否为空,如果为空,则需要等待队列非空条件变量发出通知。
上面是一些最主要的成员,另外还需要考虑限制队列长度的变量,以及一些其他的接口,例如查询队列状态(满或者空),队列中任务数,以及清空队列等。
实现
#ifndef INCLUDE_SYNCQUEUE__
#define INCLUDE_SYNCQUEUE__
#include <queue>
#include <mutex>
#include <condition_variable>
namespace MyThreadPool {
template <typename T>
class SyncQueue {
public:
SyncQueue(int max_size): max_size_{
max_size} {
}
void Push(