在await语句之后使用时,Task.Result是否会产生任何影响?

时间:2017-07-20 11:29:01

标签: c# .net multithreading async-await

我正在尝试使用概念验证应用程序,我想知道以下两种异步方法的内部结构。

Task.Result DoSomethingAsync1()会导致任何问题吗?

我已经阅读了Task.Result可能导致的一些阻塞和死锁问题,但我认为在这种情况下它不会,因为它运行在一个单独的任务上并且有一个{{1} }已经确保结果的任务。

这里的主要目标是在单独的任务上运行异步方法,因为此操作不依赖于主asp.net线程,更重要的是捕获await抛出的任何异常

此外,在DoSomethingThatTakesReallyLong() ActionResult中,我应该设置DoSomething()吗?

在下面的场景中,我是否需要注意隐藏的瓶颈/问题?

更新

我已经解决了我在这里输入问题时所做的拼写错误。 (返回用户对象而不是任务)

另外,在实际应用程序中,我无权将所有更高级别的方法一次性转换为.ConfigureAwait(false);。所以,这是我打算一步一步地做的事情,从不依赖于主线程的操作开始。

我非常感谢所有最佳做法的答案,但这是一个小游乐场代码,我的主要问题是要知道asyncDoSomethingAsync1()之间是否存在可能导致任何问题的内部差异在一定条件下的问题。

从阅读评论和答案,我得知一点没有太大区别。

DoSomethingAsync2()

https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

由于

2 个答案:

答案 0 :(得分:7)

  

如果任务已经完成,Task.Result会导致任何问题吗?

dff <- structure(list(Seedling = c("King Edward", "King Edward", "King Edward", "King Edward", "King Edward", "King Edward", "King Edward", "King Edward", "Setanta", "Setanta", "Setanta", "Sarpo Mira", "T5821/11", "T5821/11", "T5821/11", "T5821/11", NA, "T5821/11", "T5821/11", NA, "T5821/11", "Setanta", "Setanta", "T5821/11", "Setanta", "T5821/11", "T5821/11", "Setanta", NA, "Setanta", "Setanta", "Setanta", "King Edward", "King Edward", "King Edward", "King Edward", "T5821/11", "T5821/11", "T5821/11", "T5821/11", "Setanta", "Setanta", "Setanta", "Setanta", "T5821/11", "T5821/11", "T5821/11", "T5821/11", "Setanta", "Setanta", "Setanta", "Setanta", "King Edward", "King Edward", "King Edward", "King Edward", "Setanta", "Setanta", "Setanta", "Setanta", "King Edward", "King Edward", "King Edward", "King Edward", "King Edward", "King Edward", "King Edward", "King Edward", "Setanta", "Setanta", "Setanta", "Setanta", "Setanta", "Setanta", "Setanta", "Setanta", "King Edward", "King Edward", "King Edward", "King Edward", "King Edward", "King Edward", "King Edward", "King Edward", "Setanta", "Setanta", "Setanta", "Setanta", "Setanta", "Setanta", "Setanta", "Setanta", "King Edward", "King Edward", "King Edward", "King Edward", "King Edward", "King Edward", "King Edward", "King Edward", "T5821/11", "T5821/11", "T5821/11", "T5821/11", "King Edward", "King Edward", "King Edward", "King Edward", "King Edward", "King Edward", "King Edward", "King Edward", "Setanta", "Setanta", "Setanta", "Setanta", "King Edward", "King Edward", "King Edward", "King Edward", "Setanta", "Setanta", "Setanta", "Setanta", "King Edward", "King Edward", "King Edward", "King Edward", NA, NA, NA, NA, "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "T5821/11", "T5821/11", "T5821/11", "T5821/11", "T5821/11", "T5821/11", "T5821/11", "T5821/11", "T5821/11", "T5821/11", "T5821/11", "T5821/11", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "Sarpo Mira", "King Edward", "King Edward", "King Edward", "King Edward"), Treatment = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 3L, 3L, 4L, 4L, 4L, 4L, 4L, NA, 1L, 1L, NA, 1L, 2L, 2L, 1L, 2L, 2L, 2L, 2L, NA, 3L, 3L, 3L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, 1L, 1L, 1L, 1L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 3L, 3L, 3L, 3L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, 2L, 2L, 2L, 2L, 4L, 4L, 4L, 4L, 3L, 3L, 3L, 3L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, NA, NA, NA, NA, 4L, 4L, 4L, 4L, 3L, 3L, 3L, 3L, 2L, 2L, 2L, 2L, 4L, 4L, 4L, 4L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, 2L, 2L, 2L, 2L ), Genotype = c("6 A1", NA, "8 A1", "8 A1", "8 A1", "8 A1", "8 A1", "8 A1", "6 A1", "6 A1", "13 A2", "13 A2", "13 A2", NA, NA, "13 A2", NA, "13 A2", "13 A2", "8 A1", "13 A2", "6 A1", "6 A1", "13 A2", "13 A2", "8 A1", NA, "13 A2", "8 A1", "13 A2", "8 A1", "13 A2", "8 A1", "13 A2", "13 A2", "13 A2", "8 A1", "13 A2", "13 A2", "8 A1", "6 A1", "13 A2", "6 A1", "6 A1", "13 A2", "13 A2", NA, "13 A2", "13 A2", "6 A1", "6 A1", "13 A2", NA, "8 A1", "8 A1", "13 A2", "8 A1", "8 A1", "6 A1", "13 A2", "8 A1", "8 A1", "8 A1", "8 A1", "6 A1", "8 A1", "8 A1", "8 A1", "13 A2", "13 A2", "13 A2", "13 A2", "6 A1", "6 A1", "6 A1", "13 A2", "8 A1", "8 A1", "8 A1", "8 A1", "6 A1", "6 A1", NA, "8 A1", "6 A1", "6 A1", "8 A1", "6 A1", "8 A1", "8 A1", "8 A1", "13 A2", "13 A2", "8 A1", "13 A2", "13 A2", "13 A2", "13 A2", "6 A1", "13 A2", "6 A1", "6 A1", "8 A1", "8 A1", "8 A1", "8 A1", "8 A1", "6 A1", "8 A1", "8 A1", "6 A1", "8 A1", "8 A1", "8 A1", "6 A1", "13 A2", "8 A1?", "8 A1", "8 A1", "8 A1?", "13 A2", "6 A1", "13 A2", "13 A2", "8 A1", "8 A1", NA, "8 A1", "13 A2", "13 A2", "13 A2", "13 A2", "13 A2", "13 A2", "13 A2", "13 A2", "13 A2", NA, "13 A2", NA, "13 A2", "13 A2", "13 A2", NA, "13 A2", "13 A2", "13 A2", "13 A2", "13 A2", NA, "13 A2", "13 A2", "13 A2", NA, "13 A2", "13 A2", "13 A2", "6 A1", "6 A1", NA, "13 A2", "13 A2", "13 A2", "13 A2", NA, "13 A2", "13 A2", "13 A2", NA, NA, NA, NA, NA, NA, "13 A2", NA, NA, "13 A2", NA, NA)), .Names = c("Seedling", "Treatment", "Genotype"), row.names = c(NA, -180L), class = c("tbl_df", "tbl", "data.frame")) 将同步等待任务完成(与Task<T>.Result相同),然后返回结果(与task.Wait()相同)。

由于您的任务已经完成,因此await taskResult之间只有一个重要区别:await将在Result中包含例外。出于这个原因(以及使代码更加重构安全),我尽可能使用AggregateException而不是await。也就是说,采用Result方法。

  

我已经阅读了一些关于Task.Result可能导致的阻塞和死锁问题的内容,但我认为在这种情况下它不会,因为它在一个单独的任务上运行并且已经在等待任务确保结果。

它不会因为它在线程池上下文中运行

  

所以,这是我打算一步一步地做的事情,从不依赖主线程的操作开始。

您可能会发现我的brownfield async文章很有帮助。在那篇文章中,我将这种技术称为“线程池黑客”。

  

在下面的场景中,我是否需要注意隐藏的瓶颈/问题?

一些:

You shouldn't use StartNew. Use Task.Run instead

You shouldn't fire-and-forget on ASP.NET(即调用异步方法而不消耗其任务)。

You shouldn't expose fake-asynchronous methods(即具有异步签名但通过阻塞线程池线程实现的方法)。 Instead of using Task.Run to implement a method, use Task.Run to call a method

结合这三点,线程池黑客的正确结构是DoSomethingAsync2Task.Run一起使用(就像GetAwaiter().GetResult()但是避免了Result包装器) :

AggregateException

作为最后一个问题,只要在ASP.NET应用程序中使用此模式,应用程序的可伸缩性就会降低。 Instead of using one thread during DoSomethingThatTakesReallyLong, your app is now using two; this may also throw off the ASP.NET thread pool heuristics。在向public ActionResult Index() { var r = Task.Run(() => LongRunner.LongRunnerInstance.DoSomethingThatTakesReallyLong()) .GetAwaiter().GetResult(); return View(); } 过渡期间,这可能是可以接受的,但请记住,以此作为完成过渡的动力。

答案 1 :(得分:2)

正常用法是这样的:

private static async Task<User> DoSomethingAsync3()
{
    try
    {
        var user = await Task.Run(() => LongRunner.LongRunnerInstance.DoSomethingThatTakesReallyLong());
        Console.WriteLine(user.Id);
        return user;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex);
    }
}

此外,如果您要正确遵循异步模式,则应将索引更改为:

public async Task<ActionResult> Index()
{
     await DoSomethingAsync3();
     return View();
}