并行工作流性能分析

在本文中,将探讨.NET工作流框架中的WorkflowApplication对象如何异步执行多个工作流,并比较不同执行方法的执行时间。

环境要求

为了跟随本文,需要安装以下软件:

  • Visual Studio 2010 或更高版本
  • .NETFramework 4.0 或更高版本

.NET工作流框架4.0中,WorkflowApplication类被用来运行工作流(也可以使用WorkflowInvoker)。使用WorkflowApplication.Run方法,可以更好地控制工作流的执行。在WorkflowApplication中,可以设置CompletedAbortedIdleOnUnhandledException等事件的处理动作。

代码实现

示例是一个C#工作流控制台应用程序。首先,需要创建三个不同的工作流,如下所示:

  1. ForeachWorkflow.xaml:这里使用了一个序列活动来创建一个简单的数学计算,然后将序列活动放入了Foreach活动中。Foreach活动将枚举集合(以itemList作为参数),并且序列将对itemList中的每个值执行一次。
  2. ParallelForeachWorkflow.xaml:与之前的工作流类似,这里也使用了一个序列活动,并将其放入了ParallelForeach活动中。ParallelForeachForeach活动的区别在于,如果ParallelForeach中的任何嵌入活动是异步的或进入空闲状态,例如一个Delay活动或一个派生自AsyncCodeActivity的活动,那么嵌入的语句将被安排在一起并异步运行。
  3. MultiThreadWorkflow.xaml:在这个工作流中,没有使用ForeachParallelForeach活动。迭代将在代码中实现。

执行工作流

WorkflowApplication是一个异步类,与在当前线程中运行工作流ActivityInvoker不同,WorkflowApplication.Run()将异步执行工作流。此外,使用WorkflowApplication有更大的控制权。以下是创建WorkflowApplication并运行工作流的代码:

WorkflowApplication workflowApp = new WorkflowApplication(new MultiThreadWorkflow()); workflowApp.Completed = WorkflowAppCompleted; workflowApp.Run();

在下面的代码中,numberOfThreadsRunning是一个变量,用于在工作流执行完成后递减。numberOfThreadsRunning递减直到达到0。如果是这样,ManualResetEvent被设置,主线程的执行恢复。以下是WorkflowAppCompleted方法:

static int numberOfCalcs = 20; static int numberOfThreadsRunning = numberOfCalcs; static ManualResetEvent eventDone = new ManualResetEvent(false); public static void WorkflowAppCompleted(WorkflowApplicationCompletedEventArgs e) { if (e.CompletionState == ActivityInstanceState.Faulted) { Trace.WriteLine(string.Format("Workflow {0} Terminated.", e.InstanceId.ToString())); Trace.WriteLine(string.Format("Exception: {0}\n{1}", e.TerminationException.GetType().FullName, e.TerminationException.Message)); } else if (e.CompletionState == ActivityInstanceState.Canceled) { Trace.WriteLine("Canceled!"); } else { if (Interlocked.Decrement(ref numberOfThreadsRunning) == 0) eventDone.Set(); } }

注意:在上述代码中,Interlocked.Decrement用于使numberOfThreadsRunning的修改互斥。

执行结果

以下是运行程序的结果:

Number of Threads: 100 **** Simple Foreach, Elapsed Time: 00:01:40.3350970 **** Parallel Foreach, Elapsed Time: 00:01:40.0157880 **** MultiThread Workflow, Elapsed Time: 00:00:10.0113136

可以看到,多线程工作流明显比其他方法更快!

讨论

这里出现的问题可能是,为什么ForeachParallelForeach活动的结果几乎一样?原因是这两个活动中的工作流实例都是在单个线程上运行的。工作流运行时在该线程上使用类似队列的结构调度工作。当ParallelForEach活动执行时,它只是简单地将其子活动调度到运行时。因为活动执行阻塞的、同步的工作,它们阻塞了工作流正在运行的线程。解决方案是使用WorkflowApplication对象异步运行工作流。另一种方法是继承LongRunningCalcActivityAsyncCodeActivity而不是CodeActivity,以便在活动中运行在不同的线程。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485