C# 等待每个循环的异步调用
我有一个检索部署列表的方法。对于每个部署,我希望检索关联的版本。因为所有调用都是对外部API进行的,所以我现在有一个foreach循环,在其中进行这些调用C# 等待每个循环的异步调用,c#,foreach,async-await,C#,Foreach,Async Await,我有一个检索部署列表的方法。对于每个部署,我希望检索关联的版本。因为所有调用都是对外部API进行的,所以我现在有一个foreach循环,在其中进行这些调用 public static async Task<List<Deployment>> GetDeployments() { try { var depjson = await GetJson($"{BASEURL}release/deployments?deploymentSta
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;
}
}
公共静态异步任务GetDeployments()
{
尝试
{
var depjson=await GetJson($“{BASEURL}发布/部署?部署状态=成功&definitionId=2&definitionEnvironmentId=5&minStartedTime={MinDateTime}”);
var部署=(JsonConvert.DeserializeObject(depjson))?.Value?.OrderByDescending(x=>x.DeployedOn)?.ToList();
foreach(部署中的var部署)
{
var reljson=await GetJson($“{BASEURL}release/release/{deployment.ReleaseId}”);
deployment.Release=JsonConvert.DeserializeObject(reljson);
}
返回部署;
}
捕获(例外)
{
投掷;
}
}
这一切都很好。但是,我根本不喜欢foreach循环中的wait
。我也认为这不是一个好的做法。我只是不知道如何重构它以使调用并行,因为每个调用的结果都用于设置部署的属性
如果您能就如何加快此方法提出任何建议,并尽可能避免foreach循环中的
wait
-ing,我将不胜感激。如果我理解正确,并且您希望使var reljson=wait GetJson
parralel:
试试这个:
Parallel.ForEach(deployments, (deployment) =>
{
var reljson = await GetJson($"{BASEURL}release/releases/{deployment.ReleaseId}");
deployment.Release = JsonConvert.DeserializeObject<Release>(reljson);
});
Parallel.ForEach(部署,(部署)=>
{
var reljson=await GetJson($“{BASEURL}release/release/{deployment.ReleaseId}”);
deployment.Release=JsonConvert.DeserializeObject(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(
部署,
新的并行选项{MaxDegreeOfParallelism=4},
(部署)=>
{
var reljson=await GetJson($“{BASEURL}release/release/{deployment.ReleaseId}”);
deployment.Release=JsonConvert.DeserializeObject(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();
});
Parallel.ForEach(部署,(部署,状态)=>
{
var reljson=await GetJson($“{BASEURL}release/release/{deployment.ReleaseId}”);
deployment.Release=JsonConvert.DeserializeObject(reljson);
if(无需进一步处理)state.Break();
});
您现在所做的事情没有错。但是有一种方法可以一次调用所有任务,而不是等待单个任务,然后处理它,然后等待另一个任务
以下是您如何扭转这种局面:
等待一个->处理->等待一个->处理…
进入
wait all->process->done
将其转换为:
foreach (var deployment in deployments)
{
var reljson = await GetJson($"{BASEURL}release/releases/{deployment.ReleaseId}");
deployment.Release = JsonConvert.DeserializeObject<Release>(reljson);
}
foreach(部署中的var部署)
{
var reljson=await GetJson($“{BASEURL}release/release/{deployment.ReleaseId}”);
deployment.Release=JsonConvert.DeserializeObject(reljson);
}
致:
var-deplTasks=deployments.Select(d=>GetJson($“{BASEURL}release/release/{d.ReleaseId}”);
var reljsons=wait Task.WhenAll(deplTasks);
对于(var index=0;index
首先,您要列出未完成任务的列表。然后等待它,并得到一组结果(reljson
)。然后您必须对它们进行反序列化,并将其分配给Release
通过使用wait Task.WhenAll()
您可以同时等待所有任务,因此您应该可以从中看到性能的提升
如果有输入错误,请告诉我,我没有编译此代码。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]);
}
var-deplTasks=deployments.Select(d=>GetJson($“{BASEURL}release/release/{d.ReleaseId}”);
var reljsons=wait Task.WhenAll(deplTasks);
对于(var index=0;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);
}
//异步获取部署的发布数据:
专用异步任务FetchReleaseDataAsync(部署)
{
var reljson=await GetJson($“{BASEURL}release/release/{deployment.ReleaseId}”);
返回JsonConvert.DeserializeObject(reljson);
}
//异步填充部署的发布数据:
专用异步任务FillReleaseDataAsync(部署)
{
deployment.Release=await-FetchReleaseDataAsync(部署);
}
然后,您的程序与Fcin建议的解决方案类似:
IEnumerable<Task> tasksFillDeploymentWithReleaseData = deployments.
.Select(deployment => FillReleaseDataAsync(deployment)
.ToList();
await Task.WhenAll(tasksFillDeploymentWithReleaseData);
IEnumerable tasksFillDeploymentWithReleaseData=deployments。
.Select(部署=>FillReleaseDataAsync(部署)
.ToList();
等待Task.WhenAll(tasksFillDeploymentWithReleaseData);
现在,如果第一个任务在获取发布数据时必须等待,则第二个任务开始,第三个任务等。如果第一个任务已完成获取发布数据,但其他任务正在等待其发布数据,则第一个任务已开始反序列化,并将结果分配给deployment.release,然后第一个任务完成了
例如,如果第7个任务获得了数据,但第2个任务仍在等待,则第7个任务可以反序列化数据并将其分配给deployment.Release。任务7已完成
这将一直持续到所有任务完成。使用此方法,等待时间会减少,因为一旦一个任务有了数据,它就计划开始反序列化,您可以使用、使用或创建一个任务foreachGetJson()
IEnumerable<Task> tasksFillDeploymentWithReleaseData = deployments.
.Select(deployment => FillReleaseDataAsync(deployment)
.ToList();
await Task.WhenAll(tasksFillDeploymentWithReleaseData);