随着VS2010 BETA2的发布大家对.NET 4.0技术的研究热情随之高涨。在整个.NET 4.0所引入的新技术中工作流可谓是变化最大的部分。WF4与WF3几乎可以看成是两个完全不同的产品。对于WF3的编程模型已有相关的技术书籍介绍了在网上也可以搜到有关的资源。但对于WF4却几乎找不到任何深入介绍其对象模型的文章。我以Reflector作为工具反汇编了WF4的源代码通过仔细阅读粗步理出了一个头绪在本文中进行介绍期望能起到一个抛砖引玉的作用帮助大家深入地把握WF4的技术内幕。呵呵第一次在博客园发文希望大家多多鼓励。1 Acitvity的继承树在WF4中Activity类是最顶层的基类。任何一个工作流都由至少一个Activtiy构成。以下是WF4中Activity的继承树在真实的工作流中各个Activity可以相互嵌套形成一个树型结构最底层的叶子通常就是上图中最底层类如CodeActivity的实例。最顶层的Activity类提供了一个可以供子类重写的InternalExecute()方法internal virtual voidInternalExecute(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager);子类可以重写此方法在此方法中实现各种功能这个方法在WF4内部非常重要许多东西都与它相关。为了方便地供开发者自定义业务处理逻辑诸如CodeActivity之类最底层的类另定义了一个抽象的Execute()方法protected abstract void Execute(CodeActivityContext context);当开发者自定义Activity时就可以直接地重写此方法。简言之工作流的运行就体现为Activity对象树中叶子节点Execute方法或类似的方法比如DynamicActivity是InternalExecute方法AsyncCodeActivity是BeginExecute和EndExecute方法的执行。2 WF4中工作流的执行原理首先要明确在WF4中如果使用WorkflowInvoker类来启动工作流时WorkflowInvoker.Invoke(new Workflow1());工作流Workflow1将在调用者的线程中执行。这种情况下工作流的执行类似于方法调用是最简单的执行模式。然而如果使用WorkflowApplication启动工作流工作流实例将在调用者线程之外的另一个线程中运行WorkflowApplication wpp new WorkflowApplication(new Workflow1());wpp.Run();而且这个“另外的工作线程”是线程池中的线程。不管是由哪个线程负责执行工作流有一个原则是很重要的单个工作流实例是单线程执行的哪怕诸如Parallel Activity给你一个多分支“并行”运行的假象。事实上Parallel Activity采用在单线程中“轮换执行”各分支。当一个分支进入空闲“Idle”时工作流调度器调度下一分支投入运行。所果所有分支都不包括使本分支进入Idle状态的Activtity比如有一个Delay Activity或创建了书签则Parallel Activity按从左到右的顺序执行各分支。那么构成工作流的各个Activity实例是如何执行的WF运行时在内部为每个工作流维护了一个工作项队列。然后创建一个Scheduler类的实例来负责从此工作项队列中取出和追加工作项并执行之。这里要说说这个工作项队列在Scheduler类的代码中可以找到它的声明private QuackWorkItem workItemQueue;这里有一个奇怪的QuackT泛型类我仔细看了一下其实它就是一个泛型队列但它有一点特殊之处QuackT泛型类在内部使用一个数组来保存数据private T[] items;初始时为队列分配可容纳4个T类型对象的内存空间当不断增加对象而需要扩充空间时就分配一个“当前所占内存空间*2”的新数组再将老数组中的内容复制到新数组中。很明显在两个数组中复制元素会花费系统资源我不知道为何WF4的设计者这样设计估计是他们有其他的考虑。队列中的WorkItem对象很有趣它代表一个将被执行的Activity实例这里暂时放下一会儿还会介绍它。Scheduler对象的工作可以简述如下它从队列中取出一个WorkItem对象然后将其委托给线程池中的线程如果工作流由WorkApplication以异步方式启动执行或调用者线程如果工作流由WorkflowInvoker以同步方式启动执行执行。这些线程将负责调用WorkItem所封装的Activity实例的Execute()方法或类似的方法如前所述。3 深入分析Activity执行的流程一个Activity实例到底是如何执行的一切得从WorkItem类开始。WorkItem是一个抽象基类,提供了几个抽象方法其中最重要的就是Execute()方法internal abstract class WorkItem{//……private ActivityInstance activityInstance;public abstract boolExecute(ActivityExecutor executor,BookmarkManager bookmarkManager);}上述声明中还有两个很重要的类ActivityInstance和ActivityExecutor。ActivityInstance代表着正在运行的一个Activity实例它包容一堆的internal方法可以完成Activity的执行Execute取消Cancel和放弃(Abort)的功能。 ActivityExecutor则负责调用ActivityInstance中的这些方法。WorkItem有一堆的子类这些子类又派生出“孙”类。比如其中的一个分支如下不管有几个子孙后代一般都重写了WorkItem所定义的Execute()抽象方法。我们以ExecuteRootWorkItem类为例顾名思义这应该是与工作流中最顶层的Activity相对应的WorkItem。它的Execute()方法如下所示public override bool Execute(ActivityExecutor executor, BookmarkManager bookmarkManager){return base.ExecuteBody(executor, bookmarkManager, this.resultLocation);}它将调用基类ExecuteActivityWorkItem的ExecuteBody()方法此方法的关键代码如下protected bool ExecuteBody(ActivityExecutor executor, BookmarkManager bookmarkManager, Location resultLocation){//……base.ActivityInstance.Execute(executor, bookmarkManager);//……}可以看到它直接跳去执行最顶层基类WorkItem所定义的ActivityInstance对象的Execute()方法。此方法的代码如下internal void Execute(ActivityExecutor executor, BookmarkManager bookmarkManager){//……this.Activity.InternalExecute(this, executor, bookmarkManager);}注意ActivityInstance实际上封装了一个Activity对象public sealed class ActivityInstance : ActivityInstanceMap.IActivityReference{public Activity Activity { get; internal set; }//……}所以ActivityInstance对象的Execute()方法实际上执行的是Activity对象的InternalExecute()方法。再追踪下去internal virtual void InternalExecute(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager){//……executor.ScheduleActivity(this.runtimeImplementation, instance, null, null, null);}注意上述代码是Acitivity对InternalExecute默认的实现方式它的子类比如CodeActivity通常会重写它。可以看到在ActivityInstance对象的Execute()方法中执行流程转给了从前面一路传送过来的ActivityExecutor对象由此对象的ScheduleActivity方法负责将Activity插入到工作项队列中。ActivityExecutor.ScheduleActivity方法又进行了一个“倒手”调用自己的ScheduleBody()方法private ActivityInstance ScheduleActivity(……){//……this.ScheduleBody(scheduledInstance, requiresSymbolResolution, argumentValueOverrides, resultLocation);}
探索WF4 Beta2的工作流对象模型
发布时间:2026/7/6 3:51:23
随着VS2010 BETA2的发布大家对.NET 4.0技术的研究热情随之高涨。在整个.NET 4.0所引入的新技术中工作流可谓是变化最大的部分。WF4与WF3几乎可以看成是两个完全不同的产品。对于WF3的编程模型已有相关的技术书籍介绍了在网上也可以搜到有关的资源。但对于WF4却几乎找不到任何深入介绍其对象模型的文章。我以Reflector作为工具反汇编了WF4的源代码通过仔细阅读粗步理出了一个头绪在本文中进行介绍期望能起到一个抛砖引玉的作用帮助大家深入地把握WF4的技术内幕。呵呵第一次在博客园发文希望大家多多鼓励。1 Acitvity的继承树在WF4中Activity类是最顶层的基类。任何一个工作流都由至少一个Activtiy构成。以下是WF4中Activity的继承树在真实的工作流中各个Activity可以相互嵌套形成一个树型结构最底层的叶子通常就是上图中最底层类如CodeActivity的实例。最顶层的Activity类提供了一个可以供子类重写的InternalExecute()方法internal virtual voidInternalExecute(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager);子类可以重写此方法在此方法中实现各种功能这个方法在WF4内部非常重要许多东西都与它相关。为了方便地供开发者自定义业务处理逻辑诸如CodeActivity之类最底层的类另定义了一个抽象的Execute()方法protected abstract void Execute(CodeActivityContext context);当开发者自定义Activity时就可以直接地重写此方法。简言之工作流的运行就体现为Activity对象树中叶子节点Execute方法或类似的方法比如DynamicActivity是InternalExecute方法AsyncCodeActivity是BeginExecute和EndExecute方法的执行。2 WF4中工作流的执行原理首先要明确在WF4中如果使用WorkflowInvoker类来启动工作流时WorkflowInvoker.Invoke(new Workflow1());工作流Workflow1将在调用者的线程中执行。这种情况下工作流的执行类似于方法调用是最简单的执行模式。然而如果使用WorkflowApplication启动工作流工作流实例将在调用者线程之外的另一个线程中运行WorkflowApplication wpp new WorkflowApplication(new Workflow1());wpp.Run();而且这个“另外的工作线程”是线程池中的线程。不管是由哪个线程负责执行工作流有一个原则是很重要的单个工作流实例是单线程执行的哪怕诸如Parallel Activity给你一个多分支“并行”运行的假象。事实上Parallel Activity采用在单线程中“轮换执行”各分支。当一个分支进入空闲“Idle”时工作流调度器调度下一分支投入运行。所果所有分支都不包括使本分支进入Idle状态的Activtity比如有一个Delay Activity或创建了书签则Parallel Activity按从左到右的顺序执行各分支。那么构成工作流的各个Activity实例是如何执行的WF运行时在内部为每个工作流维护了一个工作项队列。然后创建一个Scheduler类的实例来负责从此工作项队列中取出和追加工作项并执行之。这里要说说这个工作项队列在Scheduler类的代码中可以找到它的声明private QuackWorkItem workItemQueue;这里有一个奇怪的QuackT泛型类我仔细看了一下其实它就是一个泛型队列但它有一点特殊之处QuackT泛型类在内部使用一个数组来保存数据private T[] items;初始时为队列分配可容纳4个T类型对象的内存空间当不断增加对象而需要扩充空间时就分配一个“当前所占内存空间*2”的新数组再将老数组中的内容复制到新数组中。很明显在两个数组中复制元素会花费系统资源我不知道为何WF4的设计者这样设计估计是他们有其他的考虑。队列中的WorkItem对象很有趣它代表一个将被执行的Activity实例这里暂时放下一会儿还会介绍它。Scheduler对象的工作可以简述如下它从队列中取出一个WorkItem对象然后将其委托给线程池中的线程如果工作流由WorkApplication以异步方式启动执行或调用者线程如果工作流由WorkflowInvoker以同步方式启动执行执行。这些线程将负责调用WorkItem所封装的Activity实例的Execute()方法或类似的方法如前所述。3 深入分析Activity执行的流程一个Activity实例到底是如何执行的一切得从WorkItem类开始。WorkItem是一个抽象基类,提供了几个抽象方法其中最重要的就是Execute()方法internal abstract class WorkItem{//……private ActivityInstance activityInstance;public abstract boolExecute(ActivityExecutor executor,BookmarkManager bookmarkManager);}上述声明中还有两个很重要的类ActivityInstance和ActivityExecutor。ActivityInstance代表着正在运行的一个Activity实例它包容一堆的internal方法可以完成Activity的执行Execute取消Cancel和放弃(Abort)的功能。 ActivityExecutor则负责调用ActivityInstance中的这些方法。WorkItem有一堆的子类这些子类又派生出“孙”类。比如其中的一个分支如下不管有几个子孙后代一般都重写了WorkItem所定义的Execute()抽象方法。我们以ExecuteRootWorkItem类为例顾名思义这应该是与工作流中最顶层的Activity相对应的WorkItem。它的Execute()方法如下所示public override bool Execute(ActivityExecutor executor, BookmarkManager bookmarkManager){return base.ExecuteBody(executor, bookmarkManager, this.resultLocation);}它将调用基类ExecuteActivityWorkItem的ExecuteBody()方法此方法的关键代码如下protected bool ExecuteBody(ActivityExecutor executor, BookmarkManager bookmarkManager, Location resultLocation){//……base.ActivityInstance.Execute(executor, bookmarkManager);//……}可以看到它直接跳去执行最顶层基类WorkItem所定义的ActivityInstance对象的Execute()方法。此方法的代码如下internal void Execute(ActivityExecutor executor, BookmarkManager bookmarkManager){//……this.Activity.InternalExecute(this, executor, bookmarkManager);}注意ActivityInstance实际上封装了一个Activity对象public sealed class ActivityInstance : ActivityInstanceMap.IActivityReference{public Activity Activity { get; internal set; }//……}所以ActivityInstance对象的Execute()方法实际上执行的是Activity对象的InternalExecute()方法。再追踪下去internal virtual void InternalExecute(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager){//……executor.ScheduleActivity(this.runtimeImplementation, instance, null, null, null);}注意上述代码是Acitivity对InternalExecute默认的实现方式它的子类比如CodeActivity通常会重写它。可以看到在ActivityInstance对象的Execute()方法中执行流程转给了从前面一路传送过来的ActivityExecutor对象由此对象的ScheduleActivity方法负责将Activity插入到工作项队列中。ActivityExecutor.ScheduleActivity方法又进行了一个“倒手”调用自己的ScheduleBody()方法private ActivityInstance ScheduleActivity(……){//……this.ScheduleBody(scheduledInstance, requiresSymbolResolution, argumentValueOverrides, resultLocation);}