您的位置:首页 > 其它

【翻译】WF从入门到精通(第十一章):并行活动

2008-06-29 21:19 253 查看
上一篇:【翻译】WF从入门到精通(第十章):事件活动
学习完本章,你将掌握:
1.理解在工作流环境中Parallel活动是怎样执行的,并且懂得如何使用它们
2.并行执行路径中的同步数据存取和临界代码区
3.使用ConditionedActivityGroup活动去执行根据条件表达式判断执行路径的并行活动

在本书中截止目前为止,我们仅仅处理过顺序业务流程。如活动A执行后转到活动B的执行等等。我们还没看到过并行执行路径和由此通常伴随而来的错综复杂的情况。在本章中,我们将看看并行活动的处理过程,以及看看怎样对横跨并行执行路径的共享信息进行同步存取。

使用Parallel活动

当你用完某样东西需去杂货店买的时候,通常都可能只有一条结帐流水线。所有的顾客都必须通过这条唯一的结帐线来付款。在那些罕见的情况下,当有两个或更多的结帐线开放后,顾客和杂货结帐的速度会更快,因为他们是以并行的方式结帐的。
在某种意义上,工作流活动也是如此。有时,你不能以混乱的方式甚至更糟糕的随机的顺序来执行特定的活动。在这些情况下,你必须选择一个Sequence活动来容纳你的工作流。但在其它时候,你可能需要设计在同一时间(或者如我们将看到的,几乎是在同一时间)能够执行多个处理过程的流程。对于这些情况,Parallel活动是一个选择。
Parallel活动是一个组合活动,但它只支持Sequence活动作为它的子活动。(当然,你可自由地把你想使用的任何活动放到该Sequence活动中。)它至少需要两个Sequence活动。
子Sequence活动并没有在单独的线程上执行,因此Parallel活动不是一个多线程活动,相反,那些子Sequence活动在单一的一个线程上执行。WF会只对一个Parallel活动执行路径中的某一单独的活动进行处理,直到该活动完成才会切换到另一个并行执行路径中的一个单独的活动。也就是说,在某个分支内的某个单独的子活动完成后,才能安排其它分支中的另一个单独的子活动去执行(译者注:每个单独的子活动是Parallel活动执行的最小单位)。各个并行活动间真正的执行顺序是无法保证的。
这样的结果是并行执行路径不会同时被执行,因此它们并不是真正意义上的多线程中的并行执行。但是,它们执行时就像是在并行操作,并且你也可这样看待它们。把Parallel活动看成是真正意义的并行过程是最明智的:你可像对待任何多线程下的处理过程来对待并行活动。
注意:假如你需要强制指定并行执行路径间的顺序,可考虑使用SynchronizationScope活动。本章晚些时候我将对它进行演示。
在此时有个值得关注的问题:“有Delay活动会怎么样呢?”正如你知道的,顺序工作流中的Delay活动会停止执行指定的TimeoutDuration时间间隔。那这会停止Parallel活动的处理过程吗?答案是不会。延时会导致特定的顺序工作流路径将被停止,但其它并行路径会正常地继续进行处理。
考虑到我所发出过的所有这些多线程警告,你或许会认为使用Parallel活动是一个挑战。事实上,它非常容易使用。它在工作流视图设计器中呈现出来的样式和Listen活动(该活动在第10章“事件活动”中讨论过)非常相像。和EventDriven活动不同的是,你将会在它里面找到Sequence活动,除此之外,表现出来的可视化界面都是相似的。我们这就创建一个简单的例子来演示一下Parallel活动。

创建一个带有并行执行过程的新工作流应用程序
1.为了快速地演示Parallel活动,本例子使用的是一个基于控制台的Windows应用程序。我们需要下载本章源代码,源代码中包含有练习版和完整版两个版本的解决方案项目,完整版中的项目可直接运行查看运行结果,我们在此使用Visual Studio打开练习版中的解决方案项目文件。
2.在Visual Studio打开了ParallelHelloWorld解决方案后,找到Workflow1工作流并在工作流视图设计器中打开它。
3.从工具箱中拖拽一个Parallel活动到设计器界面上。
// Create the workflow instance.
WorkflowInstance instance =
workflowRuntime.CreateWorkflow(typeof(ParallelFlow.Workflow1));

// Start the workflow instance.
instance.Start();
15.编译本解决方案,修正所有的编译错误。
16.按下F5或者Ctrl+F5运行本应用程序。注意,为了能看到输出结果,你应该在Program.cs中(在Main方法内)设置一个断点。
_msg = "Hello,";
PrintMessage();
7.但是你同时还需要添加_msg字段和PrintMessage方法。在Workflow1源代码中找到它的构造器,在它的构造器下面添加下面的代码:

private string _msg = String.Empty;

private void PrintMessage()
_msg = " World!\n";
PrintMessage();
10.拖拽一个SynchronizationScope活动放到右边的Sequence活动中。
_msg = "The quick brown fox";
PrintMessage();
14.拖拽第四个Code活动,把它放入右边Sequence活动中的SynchronizationScope活动中。它的名称命名为msg4,它的ExecuteCode属性设置为Message4。
_msg = " jumps over the lazy dog.\n";
PrintMessage();
16.工作流现在就完成了。从SynchronizedHelloWorld应用程序中添加对该工作流的项目级引用。
17.打开SynchronizedHelloWorld项目中的Program.cs文件,找到下面一行代码:
Console.WriteLine("Waiting for workflow completion.");
18.在你找到的上面一行代码的下面,添加下面的代码来创建一个工作流实例:

// Create the workflow instance.
WorkflowInstance instance =
workflowRuntime.CreateWorkflow(typeof(SynchronizedFlow.Workflow1));

// Start the workflow instance.
instance.Start();
19.编译该解决方案,纠正任何编译错误。
20.执行该应用程序。你可能需要在Main方法中设置一个断点才能看到输出结果。假如输出结果中显示的这两条信息还是杂乱的,你需要确认你在两个SynchronizationScope活动(步骤4和步骤11中添加)的SynchronizationHandles属性中输入的是完全相同的字符串。
private bool _stop = false;
private Int32 _min = -1;
private Int32 _max = -1;
private bool _notificationIssued = false;

public Int32 TankMinimum

public Int32 TankMaximum
// Set the stop flag
_stop = true;
31.接下来定位到underfillAlert1的WhenConditon属性对应的CheckEmpty方法。尽管你想让CAG每次都对它的子活动的WhenCondition进行判断,但你并不想让通知(低于下限值或超过上限值时)不停地发送到用户界面上,因为这将消耗过多的CPU周期。实际上,你只想让通知在储备箱的容量状态级别发生更改后只发出一次。下面的代码就为你做这个工作,这些代码添加到CheckEmpty方法中。

// If too empty, execute
e.Result = false;
if (TankLevel <= TankMinimum)
// If too full, execute
e.Result = false;
if (TankLevel >= TankMaximum)
e.Result = !_notificationIssued;
_notificationIssued = e.Result;
} // if
33.保存所有打开的文件,编译该解决方案。
34.执行该应用程序。向上或向下拉动滑动条可对储备箱进行补充或抽空进行模拟。当储备箱中的流体超过或低于边界条件时注意观察它下面所显示的文本。
备注:我在创建这个TankMonitor应用程序的过程中,当添加事件处理程序时会感到它非常像是一个基于状态机的工作流。假如在你创建顺序工作流的过程中发现,在一个特别的业务流程中使用一点点基于状态机的处理流程会更有用的话,CAG或许就是一个既快速又简便的极好的选择,这不用重新创建并调用一个独立的基于状态机的工作流。
信不信由你,到现在你已经看到过足够多的用来解决许多和工作流任务相关的处理过程,但这些章节讨论的话题还只是加深你对WF的理解。

本章源代码:里面包含本章的练习项目和完整代码

下一篇:【翻译】WF从入门到精通(第十二章):策略和规则
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: