在本文中,将探讨.NET工作流框架中的WorkflowApplication对象如何异步执行多个工作流,并比较不同执行方法的执行时间。
为了跟随本文,需要安装以下软件:
在.NET工作流框架4.0中,WorkflowApplication类被用来运行工作流(也可以使用WorkflowInvoker)。使用WorkflowApplication.Run方法,可以更好地控制工作流的执行。在WorkflowApplication中,可以设置Completed、Aborted、Idle、OnUnhandledException等事件的处理动作。
示例是一个C#工作流控制台应用程序。首先,需要创建三个不同的工作流,如下所示:
ForeachWorkflow.xaml:这里使用了一个序列活动来创建一个简单的数学计算,然后将序列活动放入了Foreach活动中。Foreach活动将枚举集合(以itemList作为参数),并且序列将对itemList中的每个值执行一次。ParallelForeachWorkflow.xaml:与之前的工作流类似,这里也使用了一个序列活动,并将其放入了ParallelForeach活动中。ParallelForeach与Foreach活动的区别在于,如果ParallelForeach中的任何嵌入活动是异步的或进入空闲状态,例如一个Delay活动或一个派生自AsyncCodeActivity的活动,那么嵌入的语句将被安排在一起并异步运行。MultiThreadWorkflow.xaml:在这个工作流中,没有使用Foreach或ParallelForeach活动。迭代将在代码中实现。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
可以看到,多线程工作流明显比其他方法更快!
这里出现的问题可能是,为什么Foreach和ParallelForeach活动的结果几乎一样?原因是这两个活动中的工作流实例都是在单个线程上运行的。工作流运行时在该线程上使用类似队列的结构调度工作。当ParallelForEach活动执行时,它只是简单地将其子活动调度到运行时。因为活动执行阻塞的、同步的工作,它们阻塞了工作流正在运行的线程。解决方案是使用WorkflowApplication对象异步运行工作流。另一种方法是继承LongRunningCalcActivity从AsyncCodeActivity而不是CodeActivity,以便在活动中运行在不同的线程。