Poco TaskManager 源码剖析
版本:poco-1.13.3
请先阅读《Poco ThreadPool 源码剖析》,然后再看此篇。
TaskManager 类
先看头文件,了解 TaskManager 支持哪些功能:
class TaskManager
{
public:
TaskManager(const std::string& name = "",
int minCapacity = 2,
int maxCapacity = 16,
int idleTime = 60,
int stackSize = POCO_THREAD_STACK_SIZE);
/// 支持设置名称、线程池数量范围、线程空闲时长等
TaskManager(ThreadPool& pool);
/// 直接共用一个外部已存在的线程池
/// TaskManager不会接管它的生命周期
bool start(Task* pTask);
/// 启动任务
/// 这里会接管pTask的生命周期,外部调用者不再需要维护它的生命周期
/// 任务完成后,pTask被会销毁释
/// 由于其内部是直接调用的Poco::ThreadPool,所以它也不支持任务队列
/// 一旦没有空闲线程,便会立即抛出异常
void cancelAll();
/// 这里它会调用所有任务的cancel接口,通知任务取消
/// 至于任务会不会立即取消掉,取决于用户是如何响应这个回调通知的
void joinAll();
/// 直接调用的Poco::ThreadPool::joinAll()
TaskList taskList() const;
/// 所有任务的列表
int count() const;
/// 返回TaskList元素数量
void addObserver(const AbstractObserver& observer);
void removeObserver(const AbstractObserver& observer);
/// 这个是Poco非常创新的一面,为任务提供了观察者,准确来说,这应该是订阅发布模式
/// 每个Task都可以发布一则通知,只要你在外部订阅了,就能收到你感兴趣的通知
/// 通常这里都是做成回调函数、监听器之类的,Poco做了近一步的解耦合
private:
ThreadPool& _threadPool; // 任务本质上是交给ThreadPool去完成的
bool _ownPool; // 是否直接拥有成员变量_threadPool的生命(因为_threadPool有可能是外部传参进来的)
TaskList _taskList; // ThreadPool并不管理Runnable生命周期,所以只能在这里进行管理
Timestamp _lastProgressNotification; // 最后一次通知外界任务进度值的时间点(防止短时候内发布太多任务进度通知)
NotificationCenter _nc; // 通知中心,负责实现消息的订阅与发布
mutable MutexT _mutex;
};
非常庆幸,Poco终于知道管理任务的生命周期了!同时它也支持了任务的取消操作。另外添加观察者了,实现了订阅与发布模式,这是一个亮点。但遗憾的是它依旧没有任务队列。
到这里,其实不看他代码实现,也能猜出个大概:
- 给 ThreadPool 套个壳
- 接管任务对象的生命周期
- 添加观察者,实现消息的订阅与发布
这里仅关注线程池相关,对于观察者也好,通知中心也罢,这里不再深入。
TaskManager::start 函数
bool TaskManager::start(Task* pTask)
{
TaskPtr pAutoTask(pTask); // 接管生命周期
if (pTask->getOwner())
throw IllegalStateException("Task already owned by another TaskManager");
if (pTask->state() == Task::TASK_IDLE)
{
pTask->setOwner(this);
pTask->setState(Task::TASK_STARTING);
try
{
_threadPool.start(*pTask, pTask->name()); // 直接的实现是交给了Poco::ThreadPool
ScopedLockT lock(_mutex);
_taskList.push_back(pAutoTask); // 将任务对象存到列表中
return true;
}
catch (...)
{
pTask->setOwner(nullptr);
throw;
}
}
pTask->setOwner(nullptr);
return false;
}
Task
TaskManager 代码非常简单,不再赘述。接下来看看 Task 接口是如何封装的。
class Runnable
{
public:
Runnable();
virtual ~Runnable();
virtual void run() = 0;
};
// Task在Runnable基础上增加了引用计数,这样方便其生命周期管控
class Task: public Runnable, public RefCountedObject
{
public:
enum TaskState
{
TASK_IDLE, // 空闲
TASK_STARTING, // 即将开始
TASK_RUNNING, // 正在运行中
TASK_CANCELLING, // 正在取消中
TASK_FINISHED // 已完成
};
Task(const std::string& name);
const std::string& name() const;
float progress() const;
/// 获取任务进度,是区间[0, 1]之间的小数
virtual void cancel();
/// 被任务置为取消,任务并不一定会立即取消,取决于任务代码运行到哪了,有没有处理取消事件
bool isCancelled() const;
/// 判断任务是不是已经被置为取消了
TaskState state() const;
/// 任务状态
void reset();
/// 重置进度值为零,同时将任务置为空闲态
virtual void runTask() = 0;
/// 任务入口函数,你必须继承并重写该接口
void run();
/// 线程入口函数,初始化一些任务状态,其内部会去调用的runTask接口
bool hasOwner() const;
/// 判断是否属于某个TaskManager
protected:
bool sleep(long milliseconds);
/// 可中断的睡眠,一旦cancel接口被调用,这个函数会立即返回false
void setProgress(float progress);
/// 设置进度值,会给通知中心发送一条进度通知
virtual void postNotification(Notification* pNf);
/// 向通知中心发送一则通知
private:
std::string _name; // 任务名称
std::atomic<TaskManager*> _pOwner; // 所属TaskManager
std::atomic<float> _progress; // 当前进度
std::atomic<TaskState> _state; // 任务状态
Event _cancelEvent; // 取消事件
mutable FastMutex _mutex;
};
// 线程入口函数
void Task::run()
{
TaskManager* pOwner = getOwner();
if (_state.exchange(TASK_RUNNING) < TASK_RUNNING)
{
if (pOwner)
pOwner->taskStarted(this);
try
{
runTask(); // 执行真正的任务入口
}
catch (Exception& exc)
{
if (pOwner)
pOwner->taskFailed(this, exc);
}
catch (std::exception& exc)
{
if (pOwner)
pOwner->taskFailed(this, SystemException("Task::run()", exc.what()));
}
catch (...)
{
if (pOwner)
pOwner->taskFailed(this, SystemException("Task::run(): unknown exception"));
}
}
_state = TASK_FINISHED;
if (pOwner) pOwner->taskFinished(this);
}
// 向通知中心发消息
void Task::postNotification(Notification* pNf)
{
FastMutex::ScopedLock lock(_mutex);
if (_pOwner)
_pOwner.load()->postNotification(pNf); // 调用的TaskManager::postNotification
else if (pNf)
pNf->release();
}
void TaskManager::postNotification(const Notification::Ptr& pNf)
{
// _nc是TaskManager的成员变量:NotificationCenter _nc;
_nc.postNotification(pNf);
}
总结
除没有任务队列外,TaskManager 已经非常接近最终的业务层代码了。