一、前言
这篇需要先看完 线程池 即可阅读。
本文中涉及到的类有——Workflow、__WFGoTask 、 WFGoTask、ExecRequest,Executor,ExecSession、ExecQueue、SubTask,SeriesWork
PS:SeriesWork依赖SubTask,类图中没有画出
放到一起讲主要是涉及到了继承和多态,单一类的说明不容易理解,因此选取一条比较简单易懂的线来当示范
文章逻辑,先搞懂各个类的功能,然后梳理运行时的情况即可
二、类(UML)图
先看ExecQueue和ExecSeesion
三、ExecQueue和ExecSeesion
1、ExecQueue
该类主要作用是记录链表中有多少个任务,成员变量仅有list和锁
class ExecQueue
{
public:
int init();
void deinit();
private:
struct list_head task_list;
pthread_mutex_t mutex;
public:
virtual ~ExecQueue() { }
friend class Executor;
};
2、ExecSeesion
该类记录下队列用来在线程中识别队列中结点的个数然后判断是否执行第二次。
execute和handle为纯虚函数,也就是想要在线程中执行的函数,具体行为由派生类给予
class ExecSession
{
private:
virtual void execute() = 0;
virtual void handle(int state, int error) = 0;
protected:
ExecQueue *get_queue() { return this->queue; }
private:
ExecQueue *queue;
public:
virtual ~ExecSession() { }
friend class Executor;
};
四、Executor
在workflow中,线程池中执行的任务来源是由一条条的任务队列组成的,而该接口就是封装了线程池的创建销毁以及把任务队列把加入进线程池中
1、整体接口
class Executor
{
public:
int init(size_t nthreads);//初始化,即创建线程池
void deinit();//销毁线程池
int request(ExecSession *session, ExecQueue *queue);//将任务队列加进线程池
private:
struct __thrdpool *thrdpool;
private:
static void executor_thread_routine(void *context);//线程池中的线程将要执行的任务
static void executor_cancel_tasks(const struct thrdpool_task *task);//取消任务
public:
virtual ~Executor() { }
};
2、int init(size_t nthreads)和deinit()
int Executor::init(size_t nthreads)
{
if (nthreads == 0)
{
errno = EINVAL;
return -1;
}
this->thrdpool = thrdpool_create(nthreads, 0);
if (this->thrdpool)
return 0;
return -1;
}
void Executor::deinit()
{
thrdpool_destroy(Executor::executor_cancel_tasks, this->thrdpool);
}
3、request(ExecSession *session, ExecQueue *queue)
这一函数目的很简单,就是将session和queue加入到线程池中
struct ExecTaskEntry
{
struct list_head list;//内核写法,表示一个entry为一个链表结点
ExecSession *session;//记录下session
thrdpool_t *thrdpool;//记录下当前使用的线程池,以便在routine中再次加入任务
};
int Executor::request(ExecSession *session, ExecQueue *queue)
{
struct ExecTaskEntry *entry;
session->queue = queue;
entry = (struct ExecTaskEntry *)malloc(sizeof (struct ExecTaskEntry));
if (entry)
{
//entry的个数代表这个任务执行的次数,最少要执行一次
//该特性在routine体现
entry->session = session;
entry->thrdpool = this->thrdpool;
pthread_mutex_lock(&queue->mutex);
list_add_tail(&entry->list, &queue->task_list);//将该结点添加到队列中
if (queue->task_list.next == &entry->list)
{
struct thrdpool_task task = {
.routine = Executor::executor_thread_routine,
.context = queue
};
if (thrdpool_schedule(&task, this->thrdpool) < 0)//将任务加入到线程池中
{
list_del(&entry->list);
free(entry);
entry = NULL;
}
}
pthread_mutex_unlock(&queue->mutex);
}
return -!entry;
}
4、void Executor::executor_thread_routine(void *context)
这一函数就是线程池所要执行的任务,主要职能有两个
一、执行Execsession派生出的execute和handle
二、判断任务队列,即list是否还有剩余,如果有则再次将任务加入到线程池
void Executor::executor_thread_routine(void *context)
{
ExecQueue *queue = (ExecQueue *)context;
struct ExecTaskEntry *entry;
ExecSession *session;
pthread_mutex_lock(&queue->mutex);
//获得一个结点并且移出链表
entry = list_entry(queue->task_list.next, struct ExecTaskEntry, list);
list_del(&entry->list);
session = entry->session;
//如果链表还有结点就表示还有任务,则需要继续将它加入到线程池
if (!list_empty(&queue->task_list))
{
struct thrdpool_task task = {
.routine = Executor::executor_thread_routine,
.context = queue
};
__thrdpool_schedule(&task, entry, entry->thrdpool);
//该entry的释放将会发生在__thrdpool_routine中
}
else
free(entry);
pthread_mutex_unlock(&queue->mutex);
session->execute();//执行主要行为
session->handle(ES_STATE_FINISHED, 0);
}
5、void Executor::executor_cancel_tasks(const struct thrdpool_task *task)
该函数就是取消掉队列中的所有任务
void Executor::executor_cancel_tasks(const struct thrdpool_task *task)
{
ExecQueue *queue = (ExecQueue *)task->context;
struct ExecTaskEntry *entry;
struct list_head *pos, *tmp;
ExecSession *session;
list_for_each_safe(pos, tmp, &queue->task_list)
{
entry = list_entry(pos, struct ExecTaskEntry, list);
list_del(pos);
session = entry->session;
free(entry);
session->handle(ES_STATE_CANCELED, 0);
}
}
五、SubTask与SeriesWork
1、SubTask
SubTask中dispatch和done比较关键,dispatch用来分发任务,done用来执行回调任务,并且返回串行任务中的下一个任务,具体行为都有派生类给出,可以说SubTask是所有task的基类。
subtask_done负责执行done,然后执行串行任务中的下一个任务(关于串行任务具体在prallework )
class SubTask
{
public:
virtual void dispatch() = 0;
//具体行为由派生类给出,在gotask线中是往线程池分配任务
private:
virtual SubTask *done() = 0;
//该函数后续派生出来的行为主要是执行回调函数,并且返回下一个任务(SeriesWork相关)
protected:
void subtask_done();
public:
//以下三个指针后续再讲
ParallelTask *get_parent_task() const { return this->parent; }
void *get_pointer() const { return this->pointer; }
void set_pointer(void *pointer) { this->pointer = pointer; }
private:
ParallelTask *parent;
SubTask **entry;
void *pointer;
public:
SubTask()
{
this->parent = NULL;
this->entry = NULL;
this->pointer = NULL;
}
virtual ~SubTask() { }
friend class ParallelTask;
};
subtask_done
void SubTask::subtask_done()
{
SubTask *cur = this;
ParallelTask *parent;
SubTask **entry;
while (1)
{
parent = cur->parent;
entry = cur->entry;
cur = cur->done();
//处理回调函数,返回下一个任务,在gotask中,当前cur已经更新为NULL
//后面暂时不管,用处在parallel中
if (cur)
{
cur->parent = parent;
cur->entry = entry;
if (parent)
*entry = cur;
cur->dispatch();
}
else if (parent)
{
if (__sync_sub_and_fetch(&parent->nleft, 1) == 0)
{
cur = parent;
continue;
}
}
break;
}
}
2、SeriesWork
上文说到done会返回串行任务中的下一个任务,这一方法就是由该类实现的,也由此顺带了解一下该类
核心就是用一个SubTask指针的队列来存取任务,然后使用的时候就以队列的特性pop或者push任务
其中存在回调函数,执行时机是该串行任务全部执行完成后
PS:first不在queue中,所以默认有五个任务的位置,所以在运行的时候如下图
class SeriesWork
{
public:
void start()//开始串行任务
{
assert(!this->in_parallel);
this->first->dispatch();
}
void dismiss()//取消掉这条串行任务
{
assert(!this->in_parallel);
this->dismiss_recursive();
}
public:
void push_back(SubTask *task);
void push_front(SubTask *task);
public:
void *get_context() const { return this->context; }
void set_context(void *context) { this->context = context; }
public:
/* Cancel a running series. Typically, called in the callback of a task
* that belongs to the series. All subsequent tasks in the series will be
* destroyed immediately and recursively (ParallelWork), without callback.
* But the callback of this canceled series will still be called. */
virtual void cancel() { this->canceled = true; }
/* Parallel work's callback may check the cancellation state of each
* sub-series, and cancel it's super-series recursively. */
bool is_canceled() const { return this->canceled; }
/* 'false' until the time of callback. Mainly for sub-class. */
bool is_finished() const { return this->finished; }
public:
void set_callback(series_callback_t callback)
{
this->callback = std::move(callback);
}
public:
/* The next 3 functions are intended for task implementations only. */
SubTask *pop();//返回下一个任务,调用pop_task(),即done的返回值
void set_last_task(SubTask *last)
{
last->set_pointer(this);
this->last = last;
}
void unset_last_task() { this->last = NULL; }
protected:
SubTask *get_last_task() const { return this->last; }
void set_in_parallel() { this->in_parallel = true; }
void dismiss_recursive();
protected:
void *context;
series_callback_t callback;//回调函数,在该串行任务执行完后执行
private:
SubTask *pop_task();//返回下一个任务
void expand_queue();//扩展任务队列
private:
SubTask *buf[4];//默认有四个任务
SubTask *first;//第一个任务
SubTask *last;//最后的任务
SubTask **queue;//最初指向buf,超过4个任务后再对其new一个新的
int queue_size;
int front;
int back;
bool in_parallel;
bool canceled;
bool finished;
std::mutex mutex;
protected:
SeriesWork(SubTask *first, series_callback_t&& callback);
virtual ~SeriesWork();
friend class ParallelWork;
friend class Workflow;
};
2.1初始化、push_back、push_front
这里记住一点,进入串行任务队列的任务,都会使用subtask中的point指针来指向该队列,后续用来告知自己所处的队列
SeriesWork::SeriesWork(SubTask *first, series_callback_t&& cb) :
callback(std::move(cb))//回调函数
{
this->queue = this->buf;
this->queue_size = sizeof this->buf / sizeof *this->buf;
this->front = 0;
this->back = 0;
this->in_parallel = false;
this->canceled = false;
this->finished = false;
assert(!series_of(first));
first->set_pointer(this);//subtask中的指针,用来记录当前任务所在的串行任务对列
this->first = first;
this->last = NULL;
this->context = NULL;
}
void SeriesWork::push_front(SubTask *task)
{
this->mutex.lock();
if (--this->front == -1)
this->front = this->queue_size - 1;
task->set_pointer(this);//记录当前串行任务对列
this->queue[this->front] = task;
if (this->front == this->back)
this->expand_queue();
this->mutex.unlock();
}
void SeriesWork::push_back(SubTask *task)
{
this->mutex.lock();
task->set_pointer(this);//记录当前串行任务对列
this->queue[this->back] = task;
if (++this->back == this->queue_size)
this->back = 0;
if (this->front == this->back)
this->expand_queue();
this->mutex.unlock();
}
2.2、expand_queue扩容队列
void SeriesWork::expand_queue()
{
int size = 2 * this->queue_size;//扩容大小为当前大小的两倍
SubTask **queue = new SubTask *[size];
int i, j;
i = 0;
j = this->front;
do
{
queue[i++] = this->queue[j++];//将旧任务转移到新的队列
if (j == this->queue_size)
j = 0;
} while (j != this->back);
if (this->queue != this->buf)
//初始时用的是内部自带的buf,如果是多次扩容,需要清楚上次分配的空间
delete []this->queue;
this->queue = queue;
this->queue_size = size;
this->front = 0;
this->back = i;
}
2.3、pop、pop_task
弹出任务,也就是done用到的
在pop_task中,会检测是否执行回调函数
SubTask *SeriesWork::pop()
{
bool canceled = this->canceled;
SubTask *task = this->pop_task();
if (!canceled)
return task;
while (task)
{
delete task;
task = this->pop_task();
}
return NULL;
}
SubTask *SeriesWork::pop_task()
{
SubTask *task;
this->mutex.lock();
if (this->front != this->back)
{
task = this->queue[this->front];
if (++this->front == this->queue_size)
this->front = 0;
}
else
{
task = this->last;
this->last = NULL;
}
this->mutex.unlock();
if (!task)//如果task为空,则表示该串行任务结束,进而执行回调函数
{
this->finished = true;
if (this->callback)
this->callback(this);
if (!this->in_parallel)//在并行任务队列中的话,由并行队列来回收
delete this;
}
return task;
}
2.4、销毁任务队列
void SeriesWork::dismiss_recursive()
{
SubTask *task = first;
this->callback = nullptr;
do
{
delete task;
task = this->pop_task();
} while (task);
}
六、ExecRequest
此类进一步封装了executor,为上层提供简单的借口。
并且在此层给出subtask中的dispatch的具体行为,以及ExecSeesion中handle的行为
在此层,dispatch就是把任务加进线程池
class ExecRequest : public SubTask, public ExecSession
{
public:
ExecRequest(ExecQueue *queue, Executor *executor)
{
this->executor = executor;
this->queue = queue;
}
ExecQueue *get_request_queue() const { return this->queue; }
void set_request_queue(ExecQueue *queue) { this->queue = queue; }
public:
virtual void dispatch()
{
if (this->executor->request(this, this->queue) < 0)
this->handle(ES_STATE_ERROR, errno);
}
protected:
int state;
int error;
protected:
ExecQueue *queue;
Executor *executor;
protected:
virtual void handle(int state, int error)
{
this->state = state;
this->error = error;
this->subtask_done();
}
};
七、WFGOTask与__WFGOTask
WFGOTask层给出done的行为,并且用到了workflow类中的两个函数,一是开始任务,二是获得任务队列
__WFGOTask给出了ExecSeesion中的execute的具体行为,即执行go()函数
class WFGoTask : public ExecRequest
{
public:
void start()//开始任务
{
assert(!series_of(this));
Workflow::start_series_work(this, nullptr);
}
void dismiss()//取消任务
{
assert(!series_of(this));
delete this;
}
public:
void *user_data;
//如果想在任务或者回调函数中获取结果,可以用此指针来记录下信息
public:
int get_state() const { return this->state; }
int get_error() const { return this->error; }
public:
void set_callback(std::function cb)
{
this->callback = std::move(cb);
}
protected:
virtual SubTask *done()//此层给出subtask的done
{
SeriesWork *series = series_of(this);//获取当前任务所在的串行任务队列
if (this->callback)
this->callback(this);
delete this;//执行完毕后删除自身
return series->pop();//返回下一个任务
}
protected:
std::function callback;
public:
WFGoTask(ExecQueue *queue, Executor *executor) :
ExecRequest(queue, executor)
{
this->user_data = NULL;
this->state = WFT_STATE_UNDEFINED;
this->error = 0;
}
protected:
virtual ~WFGoTask() { }
};
//获取当前任务队列
static inline SeriesWork *series_of(const SubTask *task)
{
return (SeriesWork *)task->get_pointer();
}
//开始串行任务
inline void
Workflow::start_series_work(SubTask *first, series_callback_t callback)
{
new SeriesWork(first, std::move(callback));//将wfgotask加入到串行队列中
first->dispatch();//开始运行
}
class __WFGoTask : public WFGoTask
{
public:
void set_go_func(std::function func)
{
this->go = std::move(func);
}
protected:
virtual void execute()//此层提供ExecSeesion中的execute,即我们想要执行的函数
{
this->go();
}
protected:
std::function go;
public:
__WFGoTask(ExecQueue *queue, Executor *executor,
std::function&& func) :
WFGoTask(queue, executor),
go(std::move(func))
{
}
};
八、运行步骤
难点在于subtask_done中的返回下一个任务,但是在gotask中,并没有对串行任务队列进行操作所以可以暂时简单的理解这一流程