在本文中,将探讨.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
,以便在活动中运行在不同的线程。