我希望能够使用C#异步特性(TPL/async/await)以递归方式处理分层结构。以下是我尝试做的概述。
我有一个作业集合需要处理,如下所示。每个作业都有一些任务要完成,还可以有一个或多个子作业,这些子作业也有任务要完成。所有父作业和子作业都调用同一个函数来执行实际的“工作”,并且这个函数是“异步”的(代码如下)。
我有一个作业集合需要处理,如下所示。每个作业都有一些任务要完成,还可以有一个或多个子作业,这些子作业也有任务要完成。所有父作业和子作业都调用同一个函数来执行实际的“工作”,并且这个函数是“异步”的(代码如下)。
/*
* Jobs Collection
* |
* |__ Job1
* | |__ Job4
* | | |__ Job7
* | |
* | |__ Job5
* |
* |__ Job2
* | |__ Job6
* |
* |__ Job3
* |
*/
层次结构中有3个层级。
我希望能够同时处理第一层级(Job1、Job2、Job3)。
一旦它们开始并行处理,每个单独的作业将自行启动处理,等待其处理完成(重要),然后递归地继续处理其子级,直到层次结构结束。 子级依赖于父级处理的数据,因此它们会等待父级处理完成。
实际“作业”的处理(由父级和子级调用)是异步进行的,因为调用方法是异步的 - 因此不需要“new thread”(Task.StartNew())。
这是我用来演示场景的样本代码 -
public void Process()
{
WebJob[] jobs = CreateWebJobs(); // dummy jobs
// first level
Parallel.ForEach(jobs,
new ParallelOptions { MaxDegreeOfParallelism = 2 }, // parallelism hardcoded for simplicity
(job) => ExecuteJob(job));
}
private void ExecuteJob(WebJob job, [CallerMemberName] string memberName = "")
{
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine("Caller> {0} :: {1} Job> {2} :: {3} Thread> {4}", memberName, "\t", job.Name, "\t", Thread.CurrentThread.ManagedThreadId);
Task t = GetDataAsync(job);
t.Wait(); // needed such that parent response is received before children start over (?).
if (job.Children != null)
{
job.Children.ToList().ForEach((r) =>
{
r.ParentResponse = job.Response; // Children need parent's response
ExecuteJob(r);
});
}
}
private async Task GetDataAsync(WebJob j)
{
// This is just test code. Ideally it would be an external call to some "async" method
await Task.Delay(1000);
j.Response = string.Format("{0} complete", j.Name);
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("parentResp>> {0} :: {1} Job>> {2} :: {3} Thread>> {4}", j.ParentResponse, "\t", j.Name, "\t", Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("--------------");
}
private WebJob[] CreateWebJobs()
{
return new WebJob[] {
new WebJob() { Id=1, Name = "Job1", ExecURL = "http://url1",
Children = new WebJob[]
{
new WebJob()
{
Id=2, Name = "Job2", ExecURL = "http://url2",
Children = new WebJob[]
{
new WebJob() { Id=4, Name = "Job4", ExecURL = "http://url4" }
}
},
new WebJob()
{
Id=3, Name = "Job3", ExecURL = "http://url3"
}
}
},
new WebJob() { Id=5, Name = "Job5", ExecURL = "http://url5"}
};
}
- Process方法首先并行启动所有“第一级”作业。
- ExecuteJob方法接管并递归遍历子级以完成所有处理。
这个方法可以工作,但我不确定这种递归异步模式是否是一种高效的方法。我考虑避免使用t.Wait()。我尝试在t上使用ContinueWith,但据我理解,这似乎没有任何区别。我也了解了ForEachAsync模式,并想知道它是否适用。这个解决方案最终将成为一个ASP.NET Web API服务。您对这种递归异步模式有什么想法吗?