Multithreading 如何避免基于任务的程序的递归任务列表遍历?

Multithreading 如何避免基于任务的程序的递归任务列表遍历?,multithreading,algorithm,scheduled-tasks,Multithreading,Algorithm,Scheduled Tasks,我有一个带有虚拟方法的类ITask: class ITask { public: virtual void Execute() = 0; }; 我制作了一个系统,在不同的线程上分配任务,使它们并行执行。问题是,我需要一些任务在某些其他任务完成之前无法执行。单个任务可能依赖于多个父任务,因此我无法按照以下方式执行: void Task::Execute() { //do stuff //finished for(int i = 0; i < children.si

我有一个带有虚拟方法的类ITask:

class ITask
{
  public:
      virtual void Execute() = 0;
};
我制作了一个系统,在不同的线程上分配任务,使它们并行执行。问题是,我需要一些任务在某些其他任务完成之前无法执行。单个任务可能依赖于多个父任务,因此我无法按照以下方式执行:

void Task::Execute()
{
//do stuff
//finished

    for(int i = 0; i < children.size(); i++)
    {
     ThreadingSystem::QueuedTasks.push_back(children[i]);
    }
}
void任务::执行()
{
//做事
//完成
对于(int i=0;i
所以我做了这样的事情:

class Task : public ITask
{
    public:
     void Execute();

     unsigned int dependency;

     vector<Task*> children;
};
类任务:公共ITask
{
公众:
void Execute();
无符号整数依赖;
媒介儿童;
};
无效任务::执行() { //做事 //完成

for(int i = 0; i < children.size(); i++)
{
    children[i]->dependency--;
}
for(int i=0;i依赖性--;
}
}

因此,基本上只有依赖项为0U的任务才能自由执行,因此任务需要等待其所有父任务完成后才能执行。现在的问题是,该系统变得非常混乱,例如:

    for(int i = 0; i < children.size(); i++)
    {
        if(children[i]->dependency == 0U)
        {
           ThreadingSystem::QueuedTasks.push_back(children[i]);
           //either remove added task from children or set a flag in it to mark as "queued"
        }
    }
for(int i=0;i依赖项==0U)
{
ThreadingSystem::QueuedTasks.push_-back(子[i]);
//从子任务中删除已添加的任务,或在其中设置标记为“排队”
}
}
我基本上要一直调用这个函数,直到所有子函数都离开向量。第一次迭代可能只向多线程队列发送2个任务,第二次迭代可能会发送另外3个,第三次迭代可能会发送另外7个,等等。它完全不可预测,并且涉及很多分支和循环。也许关于依赖整数的整个想法都不好?

  • 使用getter/setter,而不是直接访问
    依赖项
    • 类似于
      child->AddDependency
      child->SatisfyDependency
  • child->satisfydependence
    应在依赖项计数为零时将子项添加到队列中
  • 现在,不再轮询,而是直接由“不再依赖”事件触发将子项添加到队列

您应该考虑在已经调试过的基于任务的线程池库中插入,但是,

数据结构 您可以基于要执行的原始任务构建依赖关系树。假设您希望执行TaskA,这取决于TaskB和TaskC。TaskC本身依赖于TaskD

让您的任务保存它们的子项,这些子项是它们的依赖项

class Task : public ITask
{
    public:
     void Execute();

     vector<Task*> children;
};
类任务:公共ITask
{
公众:
void Execute();
媒介儿童;
};
对于TaskA,向量子项将由TaskB和TaskC组成。TaskB没有其他子任务。TaskC已将子任务分配给它

执行 ThreadingSystem中的线程将解析任务树(从TaskA开始)并搜索叶,即没有子任务的任务。如果一个线程找到一个叶,它将确保没有其他线程可以同时运行该任务。一面旗帜可能有用

执行后,将从树中删除该叶,并搜索另一个叶

如果当前没有叶可用,则线程必须等待一个叶可用。只要一片叶子被执行,你就可以叫醒他们

ThreadingSystem在没有剩余任务时执行,即从树中删除TaskA时执行。您可以发送一个事件或取消阻止调用者或其他东西

注 正如你评论一个答案,你这样做是为了教育目的。尝试实现一个树,如果您完成了,您可以尝试实现一个(定向)图。或执行循环检测。尝试加快查找/缓存叶等的性能


或者。。。使用现有的框架

是的,我知道我可以在免费的许可证版本下使用TBB,但实际上我这样做是为了技能发展/教育目的谢谢你的建议,看起来这种方式肯定更快,但是我认为它仍然需要对每个“依赖层”进行单独的遍历?顺便说一句,将任务传递到线程,线程只是弹出一个使用interlockedincrement的并发队列。它运行得非常快,但是我想我需要一些额外的步骤来将任务从依赖关系图移动到并发队列。@DEIMOS如果在启动期间将所有叶子都推入队列中,可以加快“树遍历”。在数据结构中保存叶的父级。如果执行完成,可以查找父级并删除子级。如果没有剩余的孩子,将其添加到队列并弹出下一个。如果您在没有父对象的情况下完成任务,则完成了树。