到现在为止我们创建的活动是由WorkflowInvoker通过Invoke方法来调用,WorkflowInvoker.Invoke()方法是一个同步方法,被调用的工作流和调用者在同一个线程中。
调用工作流的另一种实现方式是使用WorkApplication类,WorkApplication让我们在另一个线程中运行工作流,并且在工作流完成、进入空闲状态、终止以及出现未处理异常时为我们提供了相应的委托。这使得我们可以更加轻松的编写多线程的服务端和客户端程序。
在本次练习中,我们将使用WorkflowApplication来作为宿主程序运行SayHello活动并观察多线程的一些表现行为。首先调整一下我们的工作流:1、返回一个个人问候的字符串;2、返回一个32位的整数(表示运行工作流实例的线程的ID)。我们可以按照上一篇文章中的方式,创建一个单元测试来验证我们的工作流线程ID。
1、创建一个测试项目检验工作流线程ID是否作为输出参数被返回
添加命名空间
using System.Threading; using System.Diagnostics;
添加方法
[TestMethod] public void ShouldReturnWorkflowThread() { var output = WorkflowInvoker.Invoke( new SayHello() { UserName = "Test" } ); Assert.IsTrue(output.ContainsKey("WorkflowThread"), "SayHello must contain an OutArgument named WorkflowThread."); var outarg = output["WorkflowThread"]; Assert.IsInstanceOfType(outarg, typeof(Int32), "WorkflowThread must be of type Int32"); Assert.AreNotEqual(0, outarg, "WorkflowThread must not be zero"); Debug.WriteLine("Test thread is " + Thread.CurrentThread.ManagedThreadId); Debug.WriteLine("Workflow thread is " + outarg.ToString() ); }
打开测试资源管理器,运行所有测试,显示结果如下:
2、在SayHello活动中添加WorkflowThread输出参数
3、添加Sequence活动
到目前为止,我们的workflow中包含一个Assign活动,但是现在我们需要两个Assign,一个为Greeting赋值,另一个为WorkflowThread赋值。所以我们需要用一个容器类型的活动包含这两个Assign活动,这个可以包含其他活动的活动有多种,今天我们使用的Sequence--顺序活动。
我们需要先移除原来的那个Assign活动,这样我们才可以添加Sequence活动到设计窗口,所以我们先剪切掉这个Assign,等到添加完Sequence活动,
大家可能会担心,一旦剪切或删除掉Assign活动,我们刚才添加的那些参数是否会丢失。其实不用担心,因为我们添加的参数是在SayHello中添加的,就好比这些是全局变量,即使你删除掉某些方法,也不会对全局变量有所影响。
4、为WorkflowThread输出参数赋值
首先请添加对System.Threading程序集的引用,这样可以避免在赋值时要使用Thread的完整类名。另外,你也肯定需要再添加一个Assign到Sequence中,下面为这个Assign的属性赋值
5、再次运行测试程序
点击已通过的测试项的“输出”按钮,可以显示输出结果:
两个Id相同,说明运行工作流宿主的线程和运行工作流活动实例的线程是同一线程。
6、更换工作流宿主为WorkflowApplication,代码如下所示:
[TestMethod] public void ShouldReturnWorkflowThread() { AutoResetEvent sync = new AutoResetEvent(false); Int32 actionThreadId = 0; IDictionary<string, object> output = null; WorkflowApplication workflowApp = new WorkflowApplication( new SayHello() { UserName = "Test" }); //创建一个Action<WorkflowApplicationCompletedEventArgs>委托,在工作流完成时调用 workflowApp.Completed = (e) => { output = e.Outputs; actionThreadId = Thread.CurrentThread.ManagedThreadId; //标记工作流已经完成,通知等待线程(本例中为主线程) sync.Set(); }; workflowApp.Run(); //阻碍主线程1秒,保证在1秒之后如果收到结束等待信号(sync被Set),主线程才会继续运行 sync.WaitOne(TimeSpan.FromSeconds(1)); Assert.IsNotNull(output, "output not set, workflow may have timed out"); Assert.IsTrue(output.ContainsKey("WorkflowThread"), "SayHello must contain an OutArgument named WorkflowThread"); // 获取输出参数WorkflowThread var outarg = output["WorkflowThread"]; Assert.IsInstanceOfType(outarg, typeof(Int32), "WorkflowThread must be of type Int32"); Assert.AreEqual(actionThreadId, (int)outarg, "WorkflowThread should equal actionThreadID"); Debug.WriteLine("Test thread is " + Thread.CurrentThread.ManagedThreadId); Debug.WriteLine("Workflow thread is " + outarg.ToString()); }
再次测试,结果如下图
可以观察到主线程和运行工作流的线程不是同一个线程
系列文章
一 Hello Workflow4
二 The CodeActivity
三 Dynamic Workflows with XAML
四 Testing Workflows
五 WorkflowApplication
六 If/Else Logic