Poco TaskManager 源码剖析

520 阅读4分钟

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终于知道管理任务的生命周期了!同时它也支持了任务的取消操作。另外添加观察者了,实现了订阅与发布模式,这是一个亮点。但遗憾的是它依旧没有任务队列。

到这里,其实不看他代码实现,也能猜出个大概:

  1. 给 ThreadPool 套个壳
  2. 接管任务对象的生命周期
  3. 添加观察者,实现消息的订阅与发布

这里仅关注线程池相关,对于观察者也好,通知中心也罢,这里不再深入。

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 已经非常接近最终的业务层代码了。