async / await会为交织语句的Task提供好处吗?

时间:2015-06-08 14:16:11

标签: c# .net asynchronous async-await task-parallel-library

我有一个在序列结束时运行的方法(通过AJAX请求调用)。在这个方法中,我保存到数据库,发送电子邮件,在其他API /数据库中查找一堆信息,将事物关联起来等等。我只是将原始方法重构为第二个修订版并使用Task s来使它异步,并在墙上时间削减长达两秒钟。我使用Task主要是因为它看起来更容易(我不是在async / await中经验丰富)而且有些任务依赖于其他任务(比如任务CD和E都取决于B的结果,它本身依赖于一个)。 Basicalll所有任务都是同时启动的(处理只是拉下电子邮件任务的Wait()调用,这种方式要求所有其他任务完成。我通常会做这样的事情,除了有八个任务:

public thing() {
    var FooTask<x> = Task<x>.Factory.StartNew(() => {
        // ...
        return x;
    });
    var BarTask<y> = Task<y>.Factory.StartNew(() => {
      // ...
      var y = FooTask.Result;
      // ...
      return y + n;
    }
    var BazTask<z> = Task<z>.Factory.StartNew(() => {
      var y = FooTask.Result;
      return y - n;
    }
    var BagTask<z> = Task<z>.Factory.StartNew(() => {
      var g = BarTask.Result;
      var h = BazTask.Result;
      return 1;
    }
    // Lots of try/catch aggregate shenanigans.
    BagTask.Wait();
    return "yay";
}

哦如果出现问题我还需要回滚以前的东西,比如如果电子邮件发送失败就删除数据库行,所以那里有几个级别的尝试/捕获。无论如何,所有这些都有效(令人惊讶的是,这一切都在第一次尝试)。我的问题是这种方法是否会因重写使用async / await而不是Task而受益。如果是这样,多重依赖性场景如何在不重新运行已经由另一个方法运行或等待的异步方法的情况下发挥作用?我猜一些共享变量?

更新

// ...行应该表明任务正在执行某些操作,例如查找数据库记录。对不起,如果不清楚。如果上下文没有预热,大约一半的任务(总共8个)可能需要5秒钟才能完成,而另一半任务只是收集/组装/处理/使用该数据。

1 个答案:

答案 0 :(得分:1)

  

如果出现问题,我还需要回滚以前的内容,比如在电子邮件发送失败时删除数据库行,因此有几个级别的尝试/捕获。

您会发现async / await(与Task.Run配对而不是StartNew)会让您的代码更清晰:

var x = await Task.Run(() => {
  ...
  return ...;
});
var y = await Task.Run(() => {
  ...
  return x + n;
});
var bazTask = Task.Run(() => {
  ...
  return y - n;
});
var bagTask = Task.Run(async () => {
  ...
  var g = y;
  var h = await bazTask;
  return 1;
}
await bagTask;
return "yay";

如果您希望完成Task.WhenAll多个任务,您还可以选择使用await。特别是错误处理对await来说更清晰,因为它不会在AggregateException中包装异常。

<强>然而

  

通过AJAX请求调用

这有点问题。 ASP.NET上应该避免使用StartNewTask.Run

  

在停留时间内剃掉了两秒钟

是的,ASP.NET上的并行处理(代码当前正在执行的操作)将使单个请求执行得更快,但会牺牲可伸缩性。如果服务器在每个请求上进行并行处理,它将无法处理尽可能多的请求。

  

保存到数据库,发送电子邮件,在其他API /数据库中查找大量信息

这些都是I / O绑定操作,而不是CPU绑定。因此,理想解决方案是创建真正的异步I / O方法,然后使用await(如有必要,请Task.WhenAll)调用它们。通过“真正异步”,我的意思是调用底层异步API(例如,HttpClient.GetStringAsync而不是WebClient.DownloadString;或实体框架的ToListAsync而不是ToList等。使用StartNewTask.Run就是我所谓的“假异步”。

一旦拥有异步API,您的顶级方法就会变得简单:

X x = await databaseService.GetXFromDatabaseAsync();
Y y = await apiService.LookupValueAsync(x);
Task<Baz> bazTask = databaseSerivce.GetBazFromDatabaseAsync(y);
Task<Bag> bagTask = apiService.SecondaryLookupAsync(y);
await Task.WhenAll(bazTask, bagTask);
Baz baz = await bazTask;
Bag bag = await bagTask;
return baz + bag;