上面的列子我們實現了基於System.Threading.Tasks下面的組件封裝的非同步並行活動AsyncParallelActivity。
為了完善這個例子我們還要實現一些並行工作流程活動的準系統
1)
設定MaxDegreeOfParallelism (並行數目控制)
2)
支援Cancellation (子任務取消)
3)
實現CacheMetaData (效能最佳化)
我們先解釋一下為什麼需要Asynchronous Programming Model (APM)模式來實現並行,簡單理解主要原因是同步執行會在同一個線程裡面執行(InProcess),可以說.net framework中的非同步編程都是利用IAsyncResult來實現的。(非同步編程和多線程的關係是什麼呢?請大家思考)
1 MaxDegreeOfParallelism
System.Threading.Tasks.ParallelOptions類型封裝了一些關於並行任務的基本配置資訊,它主要包含有CancellationToken, MaxDegreeOfParallelism,
TaskScheduler:
CancellationToken主要用於實現取消某個並發任務 (task cancellation),
MaxDegreeOfParallelism用於控制並發執行的最多任務,主要用於效能和線程資源控制
TaskScheduler用於控制子任務的執行次序,我們可以利用此類型做自己定製的Scheduler,參見
http://www.codeguru.com/csharp/article.php/c18931
首先我們來實現MaxDegreeOfParallelism,很簡單,就在我們的AsyncParallelActivity類裡面增加一個屬性MaxDegreeOfParallelism,然後傳遞給Task Factory就可以了。根據MDSN的說法, “The exception that is thrown when this MaxDegreeOfParallelism is set
to 0 or some value less than -1.”這個值不能設定成0,或者負數。我們預設把它設為0.
[RequiredArgument]
[DefaultValue(0)]
public InArgument<int> MaxDegreeOfParallelism
{
get;
set;
}
我們要修改一下我們的AsyncParallelActivity的BeginExecute方法,讓它能接收ParallelOptions參數。
同時,對於我們上面的例子,我們拖拽了2個AsyncParallelActivity執行個體,但是它們的區別僅僅是執行的方法不同,我們要修改Function參數讓它能接收一組Action.
這樣我們就拋棄了微軟的WF中的Parallel Activity,利用我們的AsyncParallelActivity實現對並行任務的封裝。
修改後的代碼為:
[RequiredArgument]
public InArgument<Action<T>[]> Functions
{
get;
set;
}
[RequiredArgument]
[DefaultValue(0)]
public InArgument<int> MaxDegreeOfParallelism
{
get;
set;
}
protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
{
var data = Data.Get(context);
var functions = Functions.Get(context);
if (data == null) throw new ArgumentNullException("Data");
if (functions == null) throw new ArgumentException("Functions");
ParallelOptions options = new ParallelOptions();
options.MaxDegreeOfParallelism = MaxDegreeOfParallelism.Get(context);
// use Task to wrap the action
System.Threading.Tasks.Task parallel = Task.Factory.StartNew((taskState) =>
{
try
{
Parallel.ForEach<Action<T>>(functions,options, singleAction => singleAction(data));
}
catch (OperationCanceledException)
{
//Exception Handler here, using Enterprise Library Exception Block...
}
}, state);
//force to call the EndExecute
parallel.ContinueWith((task) => { callback.Invoke(task); });
return parallel;
}
我們修改一下OrderService代碼,增加一些方法,用於測試MaxDegreeOfParallelism.
public class OrderService
{
public void DoPartReplication(Order order)
{
order.PartNumber = "102324";
Thread.Sleep(2000);
System.Console.WriteLine("DoPartReplication done");
}
public void DoVehicleReplication(Order order)
{
order.VehicleNumber = "V0123";
Thread.Sleep(2000);
System.Console.WriteLine("DoVehicleReplication done");
}
public void DoCustomerReplication(Order order)
{
order.Customer = "Customer123";
Thread.Sleep(2000);
System.Console.WriteLine("DoCustomerReplication done");
}
}
我們重新定義我們的主活動CloseOrderWorkflow,拋棄微軟的WF中的Parallel Activity,完全利用我們自己定製的AsyncParallelActivity,見
其中的Functions設定為:
New Action(Of Order)() {AddressOf OrderService.DoPartReplication, AddressOf OrderService.DoVehicleReplication, AddressOf OrderService.DoCustomerReplication}
它綁定了三個業務方法。
測試1)設定MaxDegreeOfParallelism=4,執行結果為:
總共執行時間為2秒,三個業務方法都執行到了。
測試2)設定MaxDegreeOfParallelism=2,執行結果為:
執行時間為4秒,因為我們設定了最大並發為2,第三個業務方法只有等待了。測試結果證明MaxDegreeOfParallelism有效。
測試代碼在這裡。
WorkflowConsole_chapter_4.zip
2 Cancellation (待續)
-胡以謙 於 2011.6.15