在微软WF4中有一个Parallel活动,它可以模拟并行执行,但可惜的是Parallel活动并不是一个真正意义上的并行计算方案,实际上Parallel利用了一个线程去分时间段执行下面的各个分支。

今天,我们要做的就是基于Parallel活动和微软.net framework 4中的并行任务Task,构造一个实际的并行工作流。并将它扩展为通用的并行工作流解决方案。

我们首先构造一个虚拟的订单处理流程,它包含主要三个活动

(1) 订单初始化

(2) 将订单有关信息拷贝到零件数据库(Part DB)

(3) 将订单有关信息拷贝到车辆数据库(Vehicle DB)

(4) 结束订单

传统的做法是构造一个服务,顺序执行(1)->(2)->(3),但在实际业务过程中,一旦订单初始化后,2,3流程没有先后次序关系,可以并发执行。

来看业务流程图 CloseOrder Workflow (FlowChart):

首先我们定义一个简单的Order类 

public class Order
{
public Guid Id {get;set;}
public string Customer { get; set; }
public string PartNumber { get; set; }
public int Quantity { get; set; }
public OrderStatus Status { get; set; }
}

public enum OrderStatus
{
Initilized,
InProcess,
Cancelled,
Failed,
Succeeded
}

然后分别模拟零件数据库同步活动和车辆数据库同步活动。 

public class PartDBReplicationActivity : NativeActivity
{
public InOutArgument<Order> OrderInActivity { get; set; }

protected override void Execute(NativeActivityContext context)
{
//Console.WriteLine("PartDBReplication Activity starts at:{0}",DateTime.Now.ToString());
Order order = context.GetValue(OrderInActivity);
order.Status
= OrderStatus.InProcess;
//Call Part service
Thread.Sleep(5000);
//Console.WriteLine("PartDBReplication Activity ends at {0}.\r\n", DateTime.Now.ToString());
}
}

public class VehicleDBReplicationActivity : NativeActivity
{
public InOutArgument<Order> OrderInActivity { get; set; }

protected override void Execute(NativeActivityContext context)
{
//Console.WriteLine("VehicleDBReplication Activity starts at:{0}", DateTime.Now.ToString());
Order order = context.GetValue(OrderInActivity);
order.Status
= OrderStatus.InProcess;
//Call Vehicle service
Thread.Sleep(5000);
//Console.WriteLine("VehicleDBReplication Activity ends at {0}.\r\n", DateTime.Now.ToString());
}
}

两个活动分别要执行5秒钟(Thread.Sleep(5000)),但是由于并行,它们实际应该总共只要花5秒。

 再来看订单初始化活动

public class OpenOrderActivity : NativeActivity
{
public InOutArgument<Order> OrderInActivity { get; set; }

protected override void Execute(NativeActivityContext context)
{
Order order
=new Order();
order.Id
=Guid.NewGuid();
order.PartNumber
="10506";
order.Customer
="CustomerA";
order.Quantity
=1;
order.Status
= OrderStatus.Initilized;
context.SetValue(OrderInActivity, order);

}
}

结束订单

public class CloseOrderActivity : NativeActivity
{
public InOutArgument<Order> OrderInActivity { get; set; }

protected override void Execute(NativeActivityContext context)
{
//Console.WriteLine("CloseOrder Activity starts at:{0}", DateTime.Now.ToString());
Order order = context.GetValue(OrderInActivity);
order.Status
= OrderStatus.Succeeded;
//Console.WriteLine("CloseOrder Activity ends at {0}.", DateTime.Now.ToString());
//Console.WriteLine("");
}
}

添加我们定制的活动到主工作流活动前请先编译项目,新的定制活动就会出现在Toolbox 的最上面。 我们运行一下整个流程

class Program
{
static void Main(string[] args)
{
DateTime dtStart
= DateTime.Now;
Console.WriteLine(
"CloseOrder workflow starts at:{0}", dtStart);
Console.WriteLine(
"--------------------------------------------------------------");
WorkflowInvoker.Invoke(
new CloseOrderWorkflow());
//WorkflowInvoker.Invoke(new CloseOrderParallelWorkflow());
Console.WriteLine("--------------------------------------------------------------");
DateTime dtEnd
= DateTime.Now;
Console.WriteLine(
"CloseOrder workflow ends at:{0}", dtEnd.ToString());
Console.WriteLine(
"Total time elapsed in seconds :{0}",( dtEnd - dtStart).Seconds.ToString());
Console.Read();
}
}

看看运行结果,

奇怪,整个活动花了10秒,Parallel活动的分支不是真正意义上的并行,主要原因我们刚才说过工作流中的Parallel利用了一个线程模拟分支并行的,我们的Thread.Sleep阻塞了其他分支的运行,导致整个流程运行实际时间等于串行执行这四个活动。

下一节我们将要利用.net framework4里面的Parallel Task去实现真正意义的并行工作流。

P.S. CloseOrderWorkflow.xaml 源代码 (项目命名空间为ParallelTask)

<Activity mc:Ignorable="sap" x:Class="ParallelTask.CloseOrderWorkflow" sap:VirtualizedContainerService.HintSize="654,676" mva:VisualBasic.Settings="Assembly references and imported namespaces for internal implementation" xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities" xmlns:av="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:local="clr-namespace:ParallelTask" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mv="clr-namespace:Microsoft.VisualBasic;assembly=System" xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:s1="clr-namespace:System;assembly=System" xmlns:s2="clr-namespace:System;assembly=System.Xml" xmlns:s3="clr-namespace:System;assembly=System.Core" xmlns:sa="clr-namespace:System.Activities;assembly=System.Activities" xmlns:sad="clr-namespace:System.Activities.Debugger;assembly=System.Activities" xmlns:sap="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation" xmlns:scg="clr-namespace:System.Collections.Generic;assembly=System" xmlns:scg1="clr-namespace:System.Collections.Generic;assembly=System.ServiceModel" xmlns:scg2="clr-namespace:System.Collections.Generic;assembly=System.Core" xmlns:scg3="clr-namespace:System.Collections.Generic;assembly=mscorlib" xmlns:sd="clr-namespace:System.Data;assembly=System.Data" xmlns:sl="clr-namespace:System.Linq;assembly=System.Core" xmlns:st="clr-namespace:System.Text;assembly=mscorlib" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Flowchart sad:XamlDebuggerXmlReader.FileName="C:\Project\Labs\Parallel Execution\ParallelTask\ParallelTask\CloseOrderWorkflow.xaml" sap:VirtualizedContainerService.HintSize="614,636">
<Flowchart.Variables>
<Variable x:TypeArguments="local:Order" Name="OrderInProcess" />
</Flowchart.Variables>
<sap:WorkflowViewStateService.ViewState>
<scg3:Dictionary x:TypeArguments="x:String, x:Object">
<x:Boolean x:Key="IsExpanded">False</x:Boolean>
<av:Point x:Key="ShapeLocation">270,2.5</av:Point>
<av:Size x:Key="ShapeSize">60,75</av:Size>
<av:PointCollection x:Key="ConnectorLocation">300,77.5 300,107.5 290,107.5 290,119</av:PointCollection>
</scg3:Dictionary>
</sap:WorkflowViewStateService.ViewState>
<Flowchart.StartNode>
<FlowStep x:Name="__ReferenceID0">
<sap:WorkflowViewStateService.ViewState>
<scg3:Dictionary x:TypeArguments="x:String, x:Object">
<av:Point x:Key="ShapeLocation">190,119</av:Point>
<av:Size x:Key="ShapeSize">200,22</av:Size>
<av:PointCollection x:Key="ConnectorLocation">290,141 290,171 290,175.5</av:PointCollection>
</scg3:Dictionary>
</sap:WorkflowViewStateService.ViewState>
<local:OpenOrderActivity sap:VirtualizedContainerService.HintSize="200,22" OrderInActivity="[OrderInProcess]" />
<FlowStep.Next>
<FlowStep x:Name="__ReferenceID2">
<sap:WorkflowViewStateService.ViewState>
<scg3:Dictionary x:TypeArguments="x:String, x:Object">
<av:Point x:Key="ShapeLocation">190,175.5</av:Point>
<av:Size x:Key="ShapeSize">200,49</av:Size>
<av:PointCollection x:Key="ConnectorLocation">290,224.5 290,254.5 290,269</av:PointCollection>
</scg3:Dictionary>
</sap:WorkflowViewStateService.ViewState>
<Parallel sap:VirtualizedContainerService.HintSize="554,86">
<local:PartDBReplicationActivity sap:VirtualizedContainerService.HintSize="200,40" OrderInActivity="[OrderInProcess]" />
<local:VehicleDBReplicationActivity sap:VirtualizedContainerService.HintSize="200,40" OrderInActivity="[OrderInProcess]" />
</Parallel>
<FlowStep.Next>
<FlowStep x:Name="__ReferenceID1">
<sap:WorkflowViewStateService.ViewState>
<scg3:Dictionary x:TypeArguments="x:String, x:Object">
<av:Point x:Key="ShapeLocation">190,269</av:Point>
<av:Size x:Key="ShapeSize">200,22</av:Size>
</scg3:Dictionary>
</sap:WorkflowViewStateService.ViewState>
<local:CloseOrderActivity sap:VirtualizedContainerService.HintSize="200,22" OrderInActivity="[OrderInProcess]" />
</FlowStep>
</FlowStep.Next>
</FlowStep>
</FlowStep.Next>
</FlowStep>
</Flowchart.StartNode>
<x:Reference>__ReferenceID0</x:Reference>
<x:Reference>__ReferenceID1</x:Reference>
<x:Reference>__ReferenceID2</x:Reference>
</Flowchart>
</Activity>
posted on 2011-05-25 14:11  胡以谦  阅读(2077)  评论(0编辑  收藏  举报