我有一个异步方法来处理聚合请求。它基本上将请求分解为单个请求,由具有相同返回类型的另一种方法处理,并将答案组合成单个响应。如果聚合请求实际上仅包含单个请求,我可以直接委托给第二种方法。
当我这样做时,由于我的方法是
async
,我无法直接从其他方法返回Task<string>
,我需要await
它,即使我的方法对结果没有做任何事情。另一方面,在聚合响应的分支中,我确实需要获取结果才能生成聚合响应,因此我确实需要 await
。
必须使用
return await
对我来说似乎不自然,并且似乎剥夺了调用者进行自己优化的机会。
我的问题是:
是否可以/建议在委托给具有相同返回类型的
return await
的分支中避免使用Task
?例如,不使用方法 async
,并使用 Task
?在第二个分支中等待
task.GetAwaiter().GetResult()
或者我不应该担心它,因为调用者的最终结果永远不会有任何明显的差异?
最终还是要等待结果。
这个例子已经被简化为使用简单的字符串操作来演示这个想法;实际上我的方法要复杂得多。
public async Task<string> ProcessCombinedRequest(List<string> requestList)
{
if (requestList.Count == 1)
{
return await ProcessSingleRequest(requestList[0]);
}
// Start all the sub-tasks concurrently
var taskList = new List<Task<string>>();
foreach (string request in requestList)
{
taskList.Add(ProcessSingleRequest(request));
}
// Wait for each of them to finish and get their results
var aggregatedResponse = new StringBuilder();
foreach (var task in taskList)
{
string singleResponse = await task;
aggregatedResponse.AppendLine(singleResponse);
}
return aggregatedResponse.ToString();
}
private async Task<string> ProcessSingleRequest(string request)
{
// Long running operation
Thread.Sleep(4000);
return request + " was processed.";
}
是的,但有一些注意事项和细微差别。您可以使用非
async
方法:
public Task<string> ProcessCombinedRequest(List<string> requestList)
{
// TODO: consider ternary conditional or switch expression, lambda, etc
// TODO: consider the empty list case?
if (requestList.Count == 1)
{
return ProcessSingleRequest(requestList[0]);
}
return ProcessCombinedRequestMulti(requestList);
}
private async Task<string> ProcessCombinedRequestMulti(List<string> requestList)
{
// Start all the sub-tasks concurrently
var taskList = new List<Task<string>>();
// .. etc as before
}
这消除了一层状态机开销,但是重要的是,它还改变了异常情况下的行为 - 例如,如果
requestList
结果是 null
,或者发生了一些其他随机错误 - 而不是返回错误的 Task<string>
,您的方法可能 throw
- 在任何一种情况下,观察到的堆栈跟踪都可能略有不同。
如果您确信不存在这种风险(也许进行一些
Debug.Assert
检查等),和非async
版本可能非常常见,和性能很重要:这是你当然可以考虑的事情。但是,如果您对影响没有信心:也许不用担心......