ror*_*.ap 3 .net c# convenience-methods task-parallel-library async-await
我经常编写具有方便方法的代码,基本上包装其他方法.这是一个简单的例子:
public class WithoutAsync
{
public static ReadOnlyCollection<Response> GetResponses(IEnumerable<Request> fromRequests)
{
var ret = new List<Response>();
foreach (Request r in fromRequests)
{
ret.Add(new Response());
}
return ret.AsReadOnly();
}
//convenience method
public static Response GetResponse(Request fromRequest)
{
return GetResponses(new Request[] {fromRequest})[0];
}
}
Run Code Online (Sandbox Code Playgroud)
现在我想await长时间运行但我无法弄清楚如何改进这种方法用于TPL:
public class WithAsync
{
public static async Task<ReadOnlyCollection<Response>> GetResponses(IEnumerable<Request> fromRequests)
{
var awaitableResponses = new List<Task<Response>>();
foreach (Request r in fromRequests)
{
awaitableResponses.Add(Task.Run<Response>(async () =>
{
await Task.Delay(10000); //simulate some long running async op.
return new Response();
}));
}
return new List<Response>(await Task.WhenAll(awaitableResponses)).AsReadOnly();
}
//convenience method
public static Task<Response> GetResponse(Request fromRequest)
{
return GetResponse(new Request[] { fromRequest });
}
}
Run Code Online (Sandbox Code Playgroud)
上面的便利方法显然不会起作用,因为它试图返回一个Task<ReadOnlyCollection<Response>>真正需要返回的时候Task<Response>.
这有效:
//convenience method
public static Task<Response> GetResponse(Request fromRequest)
{
return new Task<Response>(new Func<Response>(() => GetResponse(new Request[] { fromRequest }).Result[0]));
}
Run Code Online (Sandbox Code Playgroud)
但它看起来真的很尴尬,更重要的是,它.Result[0]可能会在UI线程上阻塞.
有没有什么好方法可以完成我想做的事情?
你试图避免使用那种"便利方法" async,但没有理由这样做.
你想要的是调用另一种方法,等到有响应然后得到第一个也是唯一一个.您可以通过制作async和使用来实现await:
async Task<Response> GetResponseAsync(Request fromRequest)
{
var responses = await GetResponsesAsync(new[] { fromRequest });
return responses.Single();
}
Run Code Online (Sandbox Code Playgroud)
虽然在这种特定情况下更好的解决方案是切换周围的事情,并让单个GetResponse实际执行单个请求的工作,并且多次GetRsponses调用它:
async Task<ReadOnlyCollection<Response>> GetResponsesAsync(IEnumerable<Request> fromRequests)
{
return (await Task.WhenAll(fromRequests.Select(GetResponse))).ToList().AsReadOnly();
}
async Task<Response> GetResponseAsync(Request fromRequest)
{
await Task.Delay(10000); //simulate some long running async op.
return new Response();
}
Run Code Online (Sandbox Code Playgroud)
笔记:
Task.Run而不是简单的async调用.async使用"Async"后缀命名方法(即GetResponseAsync).