技术开发 频道

探秘WF4 Beta2中工作流对象模型

  4.工作项队列中的工作项是如何调度执行的?

  Scheduler类负责工作项的调度执行。

  在Scheduler类的构造函数中,挂接了一个回调函数OnScheduledWork:

static Scheduler()

{

    
//……



    onScheduledWorkCallback
= Fx.ThunkCallback(new SendOrPostCallback(Scheduler.OnScheduledWork));

}

 

        在OnScheduledWork()函数中,揭露出了任务项调度是如何进行的秘密:

private static void OnScheduledWork(object state)

{

  
//取出队列中的第一个工作项

        WorkItem firstWorkItem
= scheduler.firstWorkItem;

        
if ((scheduler.workItemQueue != null) && (scheduler.workItemQueue.Count > 0))

        {

            scheduler.firstWorkItem
= scheduler.workItemQueue.Dequeue();

        }

        
else

        {

            scheduler.firstWorkItem
= null;

        }

      
//执行这一工作项

        continueAction
= scheduler.callbacks.ExecuteWorkItem(firstWorkItem);

  
//……

}

        下面是ExecuteWorkItem()方法的代码,可以看到,最后调度器还是委托activityExecutor来执行Activity的:

public Scheduler.RequestedAction ExecuteWorkItem(WorkItem workItem)

{

  

    Scheduler.RequestedAction objA
= this.activityExecutor.OnExecuteWorkItem(workItem);

    
//……

}

         ActivityExecutor的OnExecuteWorkItem()方法有很多代码,其中关键的就是以下这几句:

internal Scheduler.RequestedAction OnExecuteWorkItem(WorkItem workItem)

{

    
//……

                propertyManagerOwner.PropertyManager.SetupWorkflowThread();

                  

                
if ((abortException == null) && !workItem.Execute(this, this.bookmarkManager))

                {

                    
return Scheduler.YieldSilently;

                }

            }

          
//……

}

        我们终于发现了调用工作项的Execute()方法的语句。

  有的朋友可能会疑惑,我们的探索之旅从WorkItem.Execute()方法开始,转了一圈怎么又回到了WorkItem.Execute()方法?这样一来,调用工作项的WorkItem.Execute()方法将导致一个工作项被加入到队列中,然后当此工作项被执行时,它又将一个工作项加入到队列中,这会不会引发无限递归?

  事实上这正是我们想要的效果。因为一个工作流实例实际上就是一个层层嵌套的递归的结构,这种设计使得执行其顶层Activity对象的Execute()方法时,会将其子Activity所对应的WorkItem加入到队列中加以递归执行。

  很明显,对于那些不包容子Activity的Activity,我们应该“打断”这种递归执行的过程。WF4是怎么做到的?

  以一个实例来说明更好。 请看以下自定义的Activity:

public sealed class Prompt : CodeActivity

    {

                
public InArgument<string> Text { get; set; }



        
protected override void Execute(CodeActivityContext context)

        {

                Console.Write(Text.Get(context));

        }

    }

 

        注意上述Activity重写了基类CodeActivity的Execute()方法,此方法一执行完毕就会返回。

  前面说过,对工作项队列中WorkItem.Execute()方法的调用,最终将转换为对ActivityInstance对象的Execute()方法的调用。而ActivityInstance又包容了最终的Activity对象实例,并将调用转给这一最终对象的InternalExecute()方法,为方便起见,重贴此方法代码如下:

internal void Execute(ActivityExecutor executor, BookmarkManager bookmarkManager)

{

    
//……

    
this.Activity.InternalExecute(this, executor, bookmarkManager);

}

 

         在我们的自定义Activity中,没有重写CodeActivity的InternalExecute()方法(事实上也不可能,因为此方法是Sealed的),所以,被调用的实际上是基类CodeActivity的InternalExecute()方法。以下是CodeActivity的InternalExecute()方法代码:

internal sealed override void InternalExecute(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager)

{

    
//……

        
this.Execute(context);

//……

}

        非常清楚,它应用了多态特性,调用子类重写的Execute()方法,注意,它并没有调用最顶层Activity类所提供的InternalExecute()方法。

  所以,问题的关键在于最顶层基类Activity的InternalExecute()方法,默认情况下,此方法将会通过 ActivityExecutor.ScheduleActivity()方法的调用将一个工作项加入到队列中,但CodeActivity没调用Activity基类的InternalExecute()方法而是重写了此方法,所以就打断了“递归”调用链。

  5.小结

  不知道朋友们是不是有点昏了,没办法,WF4内部就是有这么多的弯弯绕。

  简单地说:工作流执行时,所有需要被执行的Activity会被封装为一个WorkItem,被放到一个工作项队列中,然后由WF4调度器(其实就是Scheduler类的实例)负责从此队列中取出工作项执行。

  工作项的执行可以由线程池中的线程承担。,也可以由调用者线程来承担。

  WF4内部的工作原理非常复杂,事实上我们也没有必要了解其每个技术细节。但如果能了解其大致的内部原理还是非常有用的,它能帮助我们避开陷阱,真正地把技术用好。

  对于技术,不仅要知其然,还要知其所以然。才能真正拥有了自由。

查看原文

0
相关文章