转换使用任务的便利方法
Converting convenience methods that use Tasks
我经常编写具有便捷方法的代码,这些方法基本上包装了其他方法。这是一个简单的例子:
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];
}
}
现在我想 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 });
}
}
上面的便捷方法显然行不通,因为它在真正需要return Task<Response>
时试图return a Task<ReadOnlyCollection<Response>>
。
这个有效:
//convenience method
public static Task<Response> GetResponse(Request fromRequest)
{
return new Task<Response>(new Func<Response>(() => GetResponse(new Request[] { fromRequest }).Result[0]));
}
但它看起来真的很尴尬,更重要的是,它阻塞在 .Result[0]
上,这可能在 UI 线程上。
有什么好的方法可以完成我想做的事情吗?
您正试图避免 "convenience method" async
,但没有理由这样做。
你想要的是调用另一个方法,等到有响应,然后得到第一个也是唯一一个。您可以通过 async
并使用 await
:
来做到这一点
async Task<Response> GetResponseAsync(Request fromRequest)
{
var responses = await GetResponsesAsync(new[] { fromRequest });
return responses.Single();
}
尽管在这种特定情况下更好的解决方案是切换事物并让单个 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();
}
备注:
- 我知道这是一个例子,但可能没有理由使用
Task.Run
而不是简单的 async
调用。
- 惯例是用 "Async" 后缀命名
async
方法(即 GetResponseAsync
)。
- 我还复数了 returns 集合的方法的名称。
我仍然坚持 I3arnon 的答案,因为它写得很好,内容丰富,但我想提交自己的答案,因为我意识到我是 差不多了。这是我苦苦寻找的 async
便捷方法:
//convenience method
public static async Task<Response> GetResponse(Request fromRequest)
{
return (await GetResponses(new Request[] { fromRequest }))[0];
}
我经常编写具有便捷方法的代码,这些方法基本上包装了其他方法。这是一个简单的例子:
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];
}
}
现在我想 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 });
}
}
上面的便捷方法显然行不通,因为它在真正需要return Task<Response>
时试图return a Task<ReadOnlyCollection<Response>>
。
这个有效:
//convenience method
public static Task<Response> GetResponse(Request fromRequest)
{
return new Task<Response>(new Func<Response>(() => GetResponse(new Request[] { fromRequest }).Result[0]));
}
但它看起来真的很尴尬,更重要的是,它阻塞在 .Result[0]
上,这可能在 UI 线程上。
有什么好的方法可以完成我想做的事情吗?
您正试图避免 "convenience method" async
,但没有理由这样做。
你想要的是调用另一个方法,等到有响应,然后得到第一个也是唯一一个。您可以通过 async
并使用 await
:
async Task<Response> GetResponseAsync(Request fromRequest)
{
var responses = await GetResponsesAsync(new[] { fromRequest });
return responses.Single();
}
尽管在这种特定情况下更好的解决方案是切换事物并让单个 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();
}
备注:
- 我知道这是一个例子,但可能没有理由使用
Task.Run
而不是简单的async
调用。 - 惯例是用 "Async" 后缀命名
async
方法(即GetResponseAsync
)。 - 我还复数了 returns 集合的方法的名称。
我仍然坚持 I3arnon 的答案,因为它写得很好,内容丰富,但我想提交自己的答案,因为我意识到我是 差不多了。这是我苦苦寻找的 async
便捷方法:
//convenience method
public static async Task<Response> GetResponse(Request fromRequest)
{
return (await GetResponses(new Request[] { fromRequest }))[0];
}