匿名异步,什么是正确的方式
本文关键字:方式 什么 异步 | 更新日期: 2023-09-27 18:11:14
我有一个简单的类做同步的事情,
public static class Synchronous
{
public static void DoTheWholeThing()
{
AStuff aStuff;
using (var a = new A())
{
aStuff = a.GetStuff();
}
BStuff bStuff;
using (var b = new B())
{
bStuff = b.GetStuff();
}
var combination = CombineStuff(aStuff, bStuff);
}
private static Combination CombineStuff(AStuff aStuff, BStuff bStuff)
{
//// Magic Here
}
}
显然,这段代码没有完全定义,但它确实说明了我的问题。
现在,A
和B
类都负责从不同的远程数据源检索数据。因此,A
和B
的开发人员实现了称为GetStuffAsync
的异步入口点,分别返回Task<AStuff>
和Task<BStuff>
。
我想最大限度地利用异步方法并并发调用它们,这样我就可以减少代码的总体等待时间。
到目前为止,这是我所编造的。
public static class Asynchronous
{
public async static Task DoTheWholeThing(CancellationToken cancellationToken)
{
var getAStuffTask = new Func<Task<AStuff>>(
async () =>
{
using (var a = new A())
{
return await a.GetStuffAsync(cancellationToken);
}
})();
var getBStuffTask = new Func<Task<BStuff>>(
async () =>
{
using (var b = new B())
{
return await b.GetStuffAsync(cancellationToken);
}
})();
var combination = CombineStuff(
await getAStuffTask,
await getBStuffTask);
}
private Combination CombineStuff(AStuff aStuff, BStuff bStuff)
{
//// Magic Here
}
}
除了这段代码看起来很像javascript模块模式之外,这是正确的方法吗?我不认为我应该使用Task.Run
,因为这段代码显然不是CPU限制。
这似乎有点"笨拙",我需要实例化类型化委托来做到这一点。有没有更好的办法?
编辑
我在命名函数和延续之间进退两难
只要将匿名方法提取为命名方法,代码就会变得非常简单:
public async static Task DoTheWholeThing(CancellationToken cancellationToken)
{
var getAStuffTask = GetAStuffAsync(cancellationToken);
var getBStuffTask = GetBStuffAsync(cancellationToken);
var combination = CombineStuff(
await getAStuffTask,
await getBStuffTask);
}
private static async Task<AStuff> GetAStuffAsync(CancellationToken cancellationToken)
{
using (var a = new A())
{
return await a.GetStuffAsync(cancellationToken);
}
}
private static async Task<BStuff> GetBStuffAsync(CancellationToken cancellationToken)
{
using (var b = new B())
{
return await b.GetStuffAsync(cancellationToken);
}
}
也就是说,如果你真的想要坚持匿名方法,你可以创建一个助手方法,它将允许泛型类型推断和lambda隐式地计算出委托的类型:
public async static Task DoTheWholeThing(CancellationToken cancellationToken)
{
var getAStuffTask = Start(async () =>
{
using (var a = new A())
{
return await a.GetStuffAsync(cancellationToken);
}
});
var getBStuffTask = Start(async () =>
{
using (var b = new B())
{
return await b.GetStuffAsync(cancellationToken);
}
});
var combination = CombineStuff(
await getAStuffTask,
await getBStuffTask);
}
public static Task<T> Start<T>(Func<Task<T>> asyncOperation)
{
return asyncOperation();
}
使用TPL延续在任务完成后立即调用Dispose
。
public async static Task DoTheWholeThing(CancellationToken cancellationToken)
{
var a = new A();
var b = new B();
// start the tasks and store them for awaiting later
var getAStuffTask = a.GetStuffAsync(cancellationToken);
var getBStuffTask = b.GetStuffAsync(cancellationToken);
// queue up continuations to dispose of the resource as soon as it is not needed
getAStuffTask.ContinueWith(() => a.Dispose());
getBStuffTask.ContinueWith(() => b.Dispose());
// await as normal
var combination = CombineStuff(
await getAStuffTask,
await getBStuffTask);
}
我不确定是否将整个方法包装在一个额外的using
块中会完成任何事情,但它可能会提供安心。
您不需要将异步调用包装在委托中以使它们立即执行。如果您直接调用GetStuffAsync
方法而不等待它们,您将得到相同的结果。
public static class Asynchronous
{
public async static Task DoTheWholeThing(CancellationToken cancellationToken)
{
using (var a = new A())
using (var b = new B()) {
var taskA = a.GetStuffAsync(cancellationToken);
var taskB = b.GetStuffAsync(cancellationToken);
await Task.WhenAll(new [] { taskA, taskB });
var combination = CombineStuff(taskA.Result, taskB.Result);
}
}
private Combination CombineStuff(AStuff aStuff, BStuff bStuff)
{
//// Magic Here
}
}
请注意,这确实使a
和b
对象在调用CombineStuff
期间保持活动状态,如@Servy所述。如果这是一个问题,可以将Task
对象的声明移出using
块,如下所示:
public static class Asynchronous
{
public async static Task DoTheWholeThing(CancellationToken cancellationToken)
{
Task taskA;
Task taskB;
using (var a = new A())
using (var b = new B()) {
taskA = a.GetStuffAsync(cancellationToken);
taskB = b.GetStuffAsync(cancellationToken);
await Task.WhenAll(new [] { taskA, taskB });
}
var combination = CombineStuff(taskA.Result, taskB.Result);
}
private Combination CombineStuff(AStuff aStuff, BStuff bStuff)
{
//// Magic Here
}
}
尽管只要a
和b
任务都在运行,而不是在它们返回时处理它们。