【问题标题】:data structures for scheduling workflow?调度工作流的数据结构?
【发布时间】:2009-06-30 15:06:38
【问题描述】:

我想知道什么样的数据结构/算法可能有助于处理以下情况;我不确定是否需要单个 FIFO、优先级队列或多个 FIFO。

我有 N 个必须通过预定义工作流程的对象。每个对象必须完成第 1 步、第 2 步、第 3 步、第 4 步等。每个步骤要么快速完成,要么涉及“等待”,这取决于要完成的外部事物(如文件操作的完成或其他任何事情) )。每个对象都保持自己的状态。如果我必须为这些对象定义一个接口,它会是这样的(下面用伪 Java 编写,但这个问题与语言无关):

public interface TaskObject
{
   public enum State { READY, WAITING, DONE };
   // READY = ready to execute next step
   // WAITING = awaiting some external condition
   // DONE = finished all steps

   public int getCurrentStep();
   // returns # of current step

   public int getEndStep();
   // returns # of step which is the DONE case.

   public State getState();
   // checks state and returns it. 
   // multiple calls will always be identical, 
   // except WAITING which can transition to READY or DONE.

   public State executeStep();
   // if READY, executes next step and returns getState().
   // otherwise, returns getState().

}

我需要编写一个单线程调度程序,在“下一个”对象上调用 executeStep()。我的问题是,我不确定应该使用什么技术来确定“下一个”对象是什么。我希望它是公平的(对于不在 WAITING 状态的对象,先到先得)。

我的直觉是要有 3 个 FIFO,READY、WAITING 和 DONE。一开始,所有对象都放在 READY 队列中,调度程序重复一个循环,将第一个对象从 READY 队列中取出,调用 executeStep(),并将其放入与 executeStep() 的结果相匹配的队列中。除了 WAITING 队列中的项目在状态发生变化时需要放入 READY 或 DONE 队列....啊!

有什么建议吗?

【问题讨论】:

  • 问题:(1) 一个对象可以从“准备好”退回到“等待”吗?还是只是从等待到准备好? (2) 您对问题的描述听起来好像每个步骤都是资源受限的,因此需要队列和调度程序。这是真的?第 2 步的可用资源是否有限?
  • 或者当你说“单线程调度器”时,你的意思是每个资源一个调度器?
  • "single-threaded scheduler" = 我不想涉及多线程问题(+ 在我的特定情况下,我认为我不能;我正在使用 PHP)。对象本身永远不会在后台执行任何代码;唯一的“后台”操作由操作系统完成。对象永远不会从“就绪”变为“等待”。
  • 致 John:是的,如果对象正在等待其他对象完成先决条件,则它可以从准备就绪进入等待。
  • @florin: ??也许是你的情况,而不是我的情况。

标签: algorithm data-structures scheduled-tasks


【解决方案1】:

如果这必须是单线程的,您可以为准备好的和等待的对象使用单个 FIFO 队列,并使用您的线程在每个对象出现时对其进行处理。如果它的状态更改为 WAITING,那么只需将其放回队列中,它将被重新处理。

类似(伪代码):

var item = queue.getNextItem();
var state = item.executeStep ();
if (state == WAITING)
    queue.AddItem (item);
else if (state == DONE)
    // add to collection of done objects

根据 executeStep 运行所需的时间,您可能需要引入延迟(不睡眠)以防止紧密的轮询循环。理想情况下,您应该让对象发布状态更改事件并完全取消轮询。

在多线程普及之前,这种时间片方法在硬件和通信软件中很常见。

【讨论】:

  • 这很容易,我只是有一个相当简单的 PHP 应用程序,我需要管理一些没有多线程的并行任务。谢谢!
【解决方案2】:

当任务对象从 WAITING 变为 READY 时,除了轮询它之外,您没有任何方法通知您,因此 WAITING 和 READY 队列实际上可能只是一个。您可以围绕它循环调用每一个上的 executeStep()。如果作为 executeStep() 的返回值,您收到 DONE,那么您将其从该队列中移除并将其粘贴到 DONE 队列中,然后忘记它。

如果您想对 READY 对象给予“更高优先级”并尝试在浪费任何资源轮询 WAITING 之前运行所有可能的 READY 对象,您可以像您说的那样维护 3 个队列,并且仅在您没有任何内容时处理 WAITING 队列READY 队列。

我个人会花一些精力来消除状态轮询,而是定义一个接口,当状态发生变化时,对象可以使用该接口通知您的调度程序。

【讨论】:

    【解决方案3】:

    您可能想研究操作系统调度程序的设计。以 Linux 和 *BSD 为例。

    Linux 调度程序的一些指针:Inside the Linux schedulerUnderstanding the Linux Kernel

    【讨论】:

      【解决方案4】:

      注意 - 这并没有解决您关于如何安排的问题,但我会使用一个单独的状态类来定义状态和转换。对象不应该知道它们应该经历什么状态。他们可以被告知他们所处的“步骤”等。

      也有一些模式。

      您应该阅读一些有关操作系统的信息 - 特别是调度程序。您的示例是该问题的缩小集合,如果您复制相关部分,它应该对您很有用。

      然后您可以添加优先级等。

      【讨论】:

      • @xandy:行得通。我见过其他类似的实现属于状态模式的描述。
      【解决方案5】:

      满足您问题要求的最简单技术是重复迭代所有 TaskObjects,在每个 TaskObjects 上调用 executeStep()。

      这只需要一个构造来保存任务对象,它可以是任何可迭代的结构,例如一个数组。

      由于 TaskObject 可以异步地从 WAITING 过渡到 READY,因此您必须轮询每个您不知道是否已完成的 TaskObject。

      不轮询 DONE TaskObjects 所获得的性能可以忽略不计。这取决于在 DONE TaskObject 上调用 executeStep() 的处理负载,应该很小。

      一个简单的循环轮询确保一旦 READY TaskObject 执行了一个步骤,它不会执行另一个步骤,直到所有其他 TaskObject 有机会执行。

      一个明显的附加要求是检测所有 TaskObject 何时处于 DONE 状态,以便您可以停止处理。

      为避免轮询 DONE TaskObjects,您需要为每个任务对象维护一个标志,或者将 TaskObjects 链接到两个队列中:READY/WAITING 和 DONE。

      如果您将 TaskObjects 存储在一个数组中,请将其设为一个记录数组,其中包含成员 DoneFlag 和 TaskObject。

      如果出于某种原因您将任务对象存储在一个队列中,并使用可用的 enqueue() 和 dequeue() 方法,那么两个队列而不是一个队列的开销可能很小。

      -阿尔。

      【讨论】:

        【解决方案6】:

        看看这个链接。

        Boost state machines vs uml

        Boost 有状态机。为什么要重新发明?

        【讨论】:

        • 哦,我通常会忽略带有 php 标签的帖子
        猜你喜欢
        • 1970-01-01
        • 2010-12-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多