我正在尝试使用概念验证应用程序,我想知道以下两种异步方法的内部结构。
Task.Result
DoSomethingAsync1()
会导致任何问题吗?
我已经阅读了Task.Result
可能导致的一些阻塞和死锁问题,但我认为在这种情况下它不会,因为它运行在一个单独的任务上并且有一个{{1} }已经确保结果的任务。
这里的主要目标是在单独的任务上运行异步方法,因为此操作不依赖于主asp.net线程,更重要的是捕获await
抛出的任何异常
此外,在DoSomethingThatTakesReallyLong()
ActionResult
中,我应该设置DoSomething()
吗?
在下面的场景中,我是否需要注意隐藏的瓶颈/问题?
更新
我已经解决了我在这里输入问题时所做的拼写错误。 (返回用户对象而不是任务)
另外,在实际应用程序中,我无权将所有更高级别的方法一次性转换为.ConfigureAwait(false);
。所以,这是我打算一步一步地做的事情,从不依赖于主线程的操作开始。
我非常感谢所有最佳做法的答案,但这是一个小游乐场代码,我的主要问题是要知道async
和DoSomethingAsync1()
之间是否存在可能导致任何问题的内部差异在一定条件下的问题。
从阅读评论和答案,我得知一点没有太大区别。
DoSomethingAsync2()
https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
由于
答案 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 task
和Result
之间只有一个重要区别: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
结合这三点,线程池黑客的正确结构是DoSomethingAsync2
与Task.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();
}