我有一个方法可以检索部署列表。对于每个部署,我都要检索相关联的发布。因为所有的调用都是对外部API进行的,所以我现在有了一个foreach-loop,这些调用就是在其中进行的。
public static async Task<List<Deployment>> GetDeployments()
{
try
{
var depjson = await GetJson($"{BASEURL}release/deployments?deploymentStatus=succeeded&definitionId=2&definitionEnvironmentId=5&minStartedTime={MinDateTime}");
var deployments = (JsonConvert.DeserializeObject<DeploymentWrapper>(depjson))?.Value?.OrderByDescending(x => x.DeployedOn)?.ToList();
foreach (var deployment in deployments)
{
var reljson = await GetJson($"{BASEURL}release/releases/{deployment.ReleaseId}");
deployment.Release = JsonConvert.DeserializeObject<Release>(reljson);
}
return deployments;
}
catch (Exception)
{
throw;
}
}
这一切都很正常。但是,我一点也不喜欢foreach-loop中的
对于如何使此方法更快,并且尽可能避免foreach-loop中的
你现在做的事情没有错。但是有一种方法可以一次调用所有任务,而不是等待单个任务,然后处理它,然后等待另一个任务。
这就是你如何转换这个:
<代码等待一个-&>;过程-&>;等待一个-&>;进程。。。/code>
成
<所有代码等待-&>;过程-&>;Done/Code>
转换:
foreach (var deployment in deployments)
{
var reljson = await GetJson($"{BASEURL}release/releases/{deployment.ReleaseId}");
deployment.Release = JsonConvert.DeserializeObject<Release>(reljson);
}
致:
var deplTasks = deployments.Select(d => GetJson($"{BASEURL}release/releases/{d.ReleaseId}"));
var reljsons = await Task.WhenAll(deplTasks);
for(var index = 0; index < deployments.Count; index++)
{
deployments[index].Release = JsonConvert.DeserializeObject<Release>(reljsons[index]);
}
首先,你列出一张未完成任务的清单。然后等待它,您将得到一个结果集合(codeReljson/code's)。然后您必须将它们反序列化并分配给
通过使用
如果有错别字请告诉我,我没有编译这段代码。
Fcin建议启动所有任务,等待它们全部完成,然后开始反序列化获取的数据。
但是,如果第一个任务已经完成,而第二个任务没有完成,并且第二个任务正在内部等待,则第一个任务可能已经开始反序列化。这将缩短您的进程无所事事地等待的时间。
所以不是:
var deplTasks = deployments.Select(d => GetJson($"{BASEURL}release/releases/{d.ReleaseId}"));
var reljsons = await Task.WhenAll(deplTasks);
for(var index = 0; index < deployments.Count; index++)
{
deployments[index].Release = JsonConvert.DeserializeObject<Release>(reljsons[index]);
}
我建议做以下细微的改变:
// async fetch the Release data of Deployment:
private async Task<Release> FetchReleaseDataAsync(Deployment deployment)
{
var reljson = await GetJson($"{BASEURL}release/releases/{deployment.ReleaseId}");
return JsonConvert.DeserializeObject<Release>(reljson);
}
// async fill the Release data of Deployment:
private async Task FillReleaseDataAsync(Deployment deployment)
{
deployment.Release = await FetchReleaseDataAsync(deployment);
}
则您的过程类似于Fcin建议的解决方案:
IEnumerable<Task> tasksFillDeploymentWithReleaseData = deployments.
.Select(deployment => FillReleaseDataAsync(deployment)
.ToList();
await Task.WhenAll(tasksFillDeploymentWithReleaseData);
现在,如果第一个任务在获取发布数据时必须等待,那么第二个任务开始,第三个任务开始等等。如果第一个任务已经完成了获取发布数据,但是其他任务正在等待它们的发布数据,那么第一个任务已经开始反序列化它,并将结果分配给deployment.release,之后第一个任务完成。
例如,如果第7个任务获得了数据,但第2个任务仍在等待,则第7个任务可以反序列化并将数据分配给Deployment.Release。任务7完成。
这样一直持续到所有任务完成为止。使用这种方法可以减少等待时间,因为一旦一个任务有了它的数据,它就被安排开始反序列化
如果我没有理解错的话,并且您希望使
请尝试以下操作:
Parallel.ForEach(deployments, (deployment) =>
{
var reljson = await GetJson($"{BASEURL}release/releases/{deployment.ReleaseId}");
deployment.Release = JsonConvert.DeserializeObject<Release>(reljson);
});
您可以限制并行执行的次数,例如:
Parallel.ForEach(
deployments,
new ParallelOptions { MaxDegreeOfParallelism = 4 },
(deployment) =>
{
var reljson = await GetJson($"{BASEURL}release/releases/{deployment.ReleaseId}");
deployment.Release = JsonConvert.DeserializeObject<Release>(reljson);
});
您可能还希望能够打破循环:
Parallel.ForEach(deployments, (deployment, state) =>
{
var reljson = await GetJson($"{BASEURL}release/releases/{deployment.ReleaseId}");
deployment.Release = JsonConvert.DeserializeObject<Release>(reljson);
if (noFurtherProcessingRequired) state.Break();
});