异步方法不是并行运行的

时间:2014-03-14 09:40:59

标签: c# asp.net .net asp.net-mvc asp.net-mvc-4

我正在尝试理解async / await,看看我是否需要在我的ASP.NET MVC 5应用程序中使用异步调用。

在使用值填充视图模型时,我有以下内容:

MyViewModel myViewModel = new MyViewModel();
myViewModel.Answer1 = await myService.CalculateAnswer1Async();
myViewModel.Answer2 = await myService.CalculateAnswer2Async();

以上两种方法背后的代码:

public async Task<int> CalculateAnswer1Async()
{
     await Task.Delay(5000);

     return 111;
}

public async Task<int> CalculateAnswer2Async()
{
     await Task.Delay(6000);

     return 222;
}

据我了解,上述两种方法将一个接一个地执行,先是CalculateAnswer1Async(),然后是CalculateAnswer2Async()。因此,完成此操作所需的最长时间应为6秒左右(CalculateAnswer2Async()完成所需的时间)?但是当我运行页面时,加载大约需要11秒,即5秒+6秒= 11秒。

有人可以帮我向我澄清一下我做错了吗?

4 个答案:

答案 0 :(得分:4)

当您await时,您将暂停当前的async方法,直到该操作完成。释放线程以执行其他工作,但在await完成之前,方法将不会继续执行。我的博客上有一个async intro,可以更详细地描述这一点。

如果要同时执行多个异步请求,则可以延迟await,直到所有任务都已启动。所以这是一个选择:

MyViewModel myViewModel = new MyViewModel();
var task1 = myService.CalculateAnswer1Async();
var task2 = myService.CalculateAnswer2Async();
myViewModel.Answer1 = await task1;
myViewModel.Answer2 = await task2;

使用Task.WhenAll稍微提高效率(通常会产生更清晰/更清晰的代码):

MyViewModel myViewModel = new MyViewModel();
var task1 = myService.CalculateAnswer1Async();
var task2 = myService.CalculateAnswer2Async();
await Task.WhenAll(task1, task2);
myViewModel.Answer1 = await task1;
myViewModel.Answer2 = await task2;

如果所有操作都返回相同类型的值(在这种情况下为int),那么您可以避免等待&#34;虚假等待&#34;使用Task.WhenAll

的结果
MyViewModel myViewModel = new MyViewModel();
var task1 = myService.CalculateAnswer1Async();
var task2 = myService.CalculateAnswer2Async();
var results = await Task.WhenAll(task1, task2);
myViewModel.Answer1 = results[0];
myViewModel.Answer2 = results[1];

答案 1 :(得分:1)

myViewModel.Answer1 = await myService.CalculateAnswer1Async();

这将阻止当前线程,直到myService.CalculateAnswer1Async()完成。 事实上,当你使用await关键字时,你告诉执行线程:“嘿等待unitl我完成了这个操作,然后你可以自由地推断下一行代码”。

编辑: 而myService.CalculateAnswer1Async();正在完成它的工作,主线程可以回到一些名为SynchronizationContext的东西,当myService.CalculateAnswer1Async()完成它的工作。它将返回并继续使用其他方法。

答案 2 :(得分:1)

在第一种方法完成后,您正在开始第二种方法CalculateAnswer2Async ,因为await是第一种方法的结果。

立即等待异步操作的结果仅在UI线程中非常有用,因为它释放UI线程以执行其他工作而不是阻止它。但它不会引入并发性。

您要做的是启动第一个方法,然后启动第二个方法,然后在两个结果上调用await

var result1 = myService.CalculateAnswer1Async();
var result2 = myService.CalculateAnswer2Async();
myViewModel.Answer1 = await result1;
myViewModel.Answer2 = await result2;

async / await不会自行引入并发性,并且所有代码都可以同步运行。但是使用asyncawait使您可以从异步调用中受益,同时仍然具有易于编写的代码,其行为大多与您对同步版本的预期相同。

答案 3 :(得分:1)

在这种情况下,等待阻止方法的执行,直到async方法完成为止,有效地逐个运行它们。

您实际想要做的是并行运行它们:

MyViewModel myViewModel = new MyViewModel();
Task<int>[] tasks = new []
{
   myService.CalculateAnswer1Async();
   myService.CalculateAnswer2Async();
};

Task.WaitAll(tasks);

myViewModel.Answer1 = tasks[0].Result;
myViewModel.Answer2 = tasks[1].Result;