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

Ren*_*Ren 2 .net c# multithreading async-await

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

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

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

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

另外,在ActionResult DoSomething(),我应该设置.ConfigureAwait(false);

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

更新

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

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

我非常感谢所有最佳实践答案,但这是一个小游乐场代码,我的主要问题是要知道是否存在内部差异DoSomethingAsync1(),DoSomethingAsync2()这可能会导致某些条件下的任何问题.

从阅读评论和答案,我得到一个想法,没有太大的区别.


  private static async Task<User> DoSomethingAsync1()
    {
        try
        {
            var longRunningTask = Task<User>.Factory.StartNew(() => LongRunner.LongRunnerInstance.DoSomethingThatTakesReallyLong());
            var user = await longRunningTask;
            //Will the below line of code cause any issue?
            Console.WriteLine(longRunningTask.Result.Id);
            return user;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
            return null;
        }
    }

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

   public ActionResult Index()
   {

    //Things that run on main Asp.NET thread

    //Things that run independently 
    //Won't be using the task returned here at the moment. 
    DoSomethingAsync1();

    //Do more things on main Asp.NET thread if needed

    return View();             

   }
Run Code Online (Sandbox Code Playgroud)

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

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

谢谢

Ste*_*ary 8

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

Task<T>.Result将同步等待任务完成(相同task.Wait()),然后返回结果(与之相同await task).

由于您的任务已经完成,因此只有一个区别Result,await这很重要:Result将异常包装在一个AggregateException.出于这个原因(并且还使代码更加重构安全),我使用await而不是Result尽可能.也就是说,采取这种DoSomethingAsync2方法.

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

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

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

你可能会发现我的棕色异步文章很有帮助.在那篇文章中,我将这种技术称为"线程池黑客".

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

一些:

你不应该使用StartNew.请Task.Run改用.

你不应该在ASP.NET上发射(即,在不消耗其任务的情况下调用异步方法).

您不应该公开伪异步方法(即具有异步签名但通过阻塞线程池线程实现的方法).而不是使用Task.Run实现一种方法,使用Task.Run调用一个方法.

结合这三个点,线程池哈克的正确结构是用Task.RunGetAwaiter().GetResult()(这只是喜欢Result,但避免了AggregateException包装):

public ActionResult Index()
{
  var r = Task.Run(() => LongRunner.LongRunnerInstance.DoSomethingThatTakesReallyLong())
      .GetAwaiter().GetResult();
  return View();             
}
Run Code Online (Sandbox Code Playgroud)

作为最后一个问题,只要在ASP.NET应用程序中使用此模式,应用程序的可伸缩性就会降低.您的应用程序现在使用两个,而不是在使用一个线程DoSomethingThatTakesReallyLong; 这也可能会抛弃ASP.NET线程池启发式.在过渡期间这可能是可以接受的async,但要记住它作为完成过渡的动力.