WorkFlow学习分享:ParallelWork

一、概括

该类继承了ParalleTask,而ParalleTask又继承了subtask,依赖Serieswork(WFGOTask文中有讲),该类主要实现串行任务的并行工作。

PS:在WFGOTask文中,对于Serieswork的工作有一个错误,在执行任务的最后还会判断last是否存在以及执行callback,所以串行任务流该为下图

该类下有个数组为subtasks,用来存储一条条的串行任务,假设现在有四条并行任务,那么该类的工作模式可以理解为

PS:ParallelWork同样也有回调函数(callback)

         

 同时,我们知道ParallelTask继承了subtask,那么ParallelTask也是一种任务。如果在串行任务流中存在ParallelTask,假设图中的last为ParallelTask,就为

 以此类推,可以一直衍生下去

二、类图

三、ParallelTask

1、成员变量和成员函数

subtasks为指针数组,每个指针用来存串行任务的第一个任务

class ParallelTask : public SubTask
{
public:
	ParallelTask(SubTask **subtasks, size_t n)
	{
		this->subtasks = subtasks;
		this->subtasks_nr = n;
	}

	SubTask **get_subtasks(size_t *n) const
	{
		*n = this->subtasks_nr;
		return this->subtasks;
	}

	void set_subtasks(SubTask **subtasks, size_t n)
	{
		this->subtasks = subtasks;
		this->subtasks_nr = n;
	}

public:
	virtual void dispatch();

protected:
	SubTask **subtasks;//指针数组,用来存取串行任务
	size_t subtasks_nr;//串行任务的条数

private:
	size_t nleft;//串行任务的条数
	friend class SubTask;
};

2、void ParallelTask::dispatch()

在这一层中多态出来了subtask中的dispatch

功能:开启subtasks中的每一条串行任务

其中每次开启任务的时候都会设置subtask中的两个指针,parent,entry,具体用途在subtask_done函数中体现

parent指向当前的的Parallel Task、entry指向当前人物流的第一个任务

void ParallelTask::dispatch()
{
	SubTask **end = this->subtasks + this->subtasks_nr;
	SubTask **p = this->subtasks;

	this->nleft = this->subtasks_nr;
	if (this->nleft != 0)
	{
		do
		{
			(*p)->parent = this;//指向当前的ParallelTask
			(*p)->entry = p;//指向当前任务流的第一个任务
			(*p)->dispatch();//此时执行的dispatch是任务流中多态出来的另一个
		} while (++p != end);
	}
	else
		this->subtask_done();
}

3、subtask_done()

在WFGOTask中我们知道,除掉一些细节的操作,一个任务的大致过程可以看成如下图

 又由WFGOTask中,可以得出,无论哪个类派生出的done,它最后都会返回下一个任务

void SubTask::subtask_done()
{
	SubTask *cur = this;
	ParallelTask *parent;
	SubTask **entry;

	while (1)
	{
		parent = cur->parent;
		entry = cur->entry;
		cur = cur->done();//返回下一个任务
        //情况1:如果存在下一个任务
		if (cur)
		{
			cur->parent = parent;//设置下一个任务的父节点,即指向当前的ParallelTask
			cur->entry = entry;//设置指向当前的串行任务的指针
            //判断当前任务是否在并行任务流中,如果存在则更新当前串行任务流中的第一个任务			
            if (parent)
				*entry = cur;

			cur->dispatch();//执行下一个任务的行为
		}
        //情况2:一条串行任务运行完成
		else if (parent)
		{
			if (__sync_sub_and_fetch(&parent->nleft, 1) == 0)
			{
				cur = parent;
				continue;
			}
		}

		break;
	}
}

3.1、情况一:存在下一个任务

在该情况下,会先设置新任务节点的指针指向,可以理解成下图

 3.2、情况二:一条串行任务流的结束

在情况二,会先判断parent指针是否有内容,即判断当前任务流是否在并行任务中。如果处在并行任务中,则对nleft减一。当为零时,则表示并行任务全部结束,于是耿欣cur的指向为parent,并且指向done,而此时执行的done为ParalleWork派生出来的done。

这里有个细节,SeriesWork中的pop,在执行完回调函数后会判断是否处在并行队列中,如果在,删除该串行流的操作由ParallelWork负责

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)
	{
		this->finished = true;

		if (this->callback)
			this->callback(this);

		if (!this->in_parallel)
			delete this;
	}

	return task;
}

4、串行任务的细节

我们知道done是由派生类衍生出来的多态类,而衍生出来的任务,有的存在回调函数,有的不存在回调函数。后者可以按照上文中的图来理解,而前者的运行流程为下图

 

四、ParallelWork

 1、成员函数和成员函数

class ParallelWork : public ParallelTask
{
public:
	void start()
	{
		assert(!series_of(this));
		Workflow::start_series_work(this, nullptr);
	}

	void dismiss()
	{
		assert(!series_of(this));
		delete this;
	}

public:
	void add_series(SeriesWork *series);//添加一条串行任务

public:
	void *get_context() const { return this->context; }
	void set_context(void *context) { this->context = context; }

public:
	SeriesWork *series_at(size_t index)
	{
		if (index < this->subtasks_nr)
			return this->all_series[index];
		else
			return NULL;
	}

	const SeriesWork *series_at(size_t index) const
	{
		if (index < this->subtasks_nr)
			return this->all_series[index];
		else
			return NULL;
	}

	SeriesWork& operator[] (size_t index)
	{
		return *this->series_at(index);
	}

	const SeriesWork& operator[] (size_t index) const
	{
		return *this->series_at(index);
	}

	size_t size() const { return this->subtasks_nr; }

public:
	void set_callback(parallel_callback_t callback)
	{
		this->callback = std::move(callback);
	}

protected:
	virtual SubTask *done();

protected:
	void *context;
	parallel_callback_t callback;

private:
	void expand_buf();

private:
	size_t buf_size;
	SeriesWork **all_series;

protected:
	ParallelWork(parallel_callback_t&& callback);
	ParallelWork(SeriesWork *const all_series[], size_t n,
				 parallel_callback_t&& callback);
	virtual ~ParallelWork();
	friend class Workflow;
};

2、构造函数

//设置回调函数,并且new出一个数组给基类的Parallel
ParallelWork::ParallelWork(parallel_callback_t&& cb) :
	ParallelTask(new SubTask *[2 * 4], 0),
	callback(std::move(cb))
{
	this->buf_size = 4;
	this->all_series = (SeriesWork **)&this->subtasks[this->buf_size];
	this->context = NULL;
}

//将穿入的 all_series赋给内部的串行任务数组
ParallelWork::ParallelWork(SeriesWork *const all_series[], size_t n,
						   parallel_callback_t&& cb) :
	ParallelTask(new SubTask *[2 * (n > 4 ? n : 4)], n),
	callback(std::move(cb))
{
	size_t i;

	this->buf_size = (n > 4 ? n : 4);
	this->all_series = (SeriesWork **)&this->subtasks[this->buf_size];
	for (i = 0; i < n; i++)
	{
		assert(!all_series[i]->in_parallel);
		all_series[i]->in_parallel = true;
		this->all_series[i] = all_series[i];
		this->subtasks[i] = all_series[i]->first;
	}

	this->context = NULL;
}

在构造函数中,我们可以发现在给SubTask的空间为2n,那多出来的n个空间用来存什么呢?

通过变量名可以知道all_series是个指针数组,里面的指针存取serieswork的指针

而all_series用的空间是subtasks多出来的n个空间,所以逻辑上可以吧任务数组的每个位置理解为

 3、expand_buf()

每次扩容涨两倍

void ParallelWork::expand_buf()
{
	SubTask **buf;
	size_t size;

	this->buf_size *= 2;
	buf = new SubTask *[2 * this->buf_size];
	size = this->subtasks_nr * sizeof (void *);
	memcpy(buf, this->subtasks, size);
	memcpy(buf + this->buf_size, this->all_series, size);

	delete []this->subtasks;
	this->subtasks = buf;
	this->all_series = (SeriesWork **)&buf[this->buf_size];
}

4、add_series(SeriesWork *series)

void ParallelWork::add_series(SeriesWork *series)
{
	if (this->subtasks_nr == this->buf_size)
		this->expand_buf();

	assert(!series->in_parallel);
	series->in_parallel = true;
	this->all_series[this->subtasks_nr] = series;
	this->subtasks[this->subtasks_nr] = series->first;
	this->subtasks_nr++;
}

5、ParallelWork::done()

SubTask *ParallelWork::done()
{
	SeriesWork *series = series_of(this);
	size_t i;

	if (this->callback)
		this->callback(this);

	for (i = 0; i < this->subtasks_nr; i++)
		delete this->all_series[i];

	this->subtasks_nr = 0;
	delete this;
	return series->pop();
}

在该称给出subtask的done的具体行为,完成三个任务

1、执行ParallelWork的回调函数

2、释放所有串行任务的空间

3、返回下一个任务

前文说到,ParallelWork也是一种任务,所以它也会返回下一个任务

6、析构函数

ParallelWork::~ParallelWork()
{
	size_t i;

	for (i = 0; i < this->subtasks_nr; i++)
	{
		this->all_series[i]->in_parallel = false;
		this->all_series[i]->dismiss_recursive();
	}

	delete []this->subtasks;
}

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值