我正在尝试使用此异步代码来测试async关键字:
public async Task<string> AsyncMethod()
{
var link = "http://www.google.com";
var webclient = new WebClient();
var result = await webclient.DownloadStringTaskAsync(new Uri(link));
return result;
}
public async Task<ActionResult> Index()
{
var a = AsyncMethod();
var b = AsyncMethod();
Task.WaitAll(a, b);
return View();
}
但是当我调试它时,调试器会命中Task.WaitAll
并且什么都不做(返回的keywork永远不会被执行)。
如果我在两个AsyncMethod&#39;之前设置等待。并删除Task.WaitAll
它的工作原理..那么我做错了什么?
答案 0 :(得分:9)
因为你的方法看起来像ASP.NET MVC控制器动作,所以我假设你在ASP.NET上运行。
默认情况下,异步方法在挂起的相同上下文(即您调用await
的位置)上恢复。在ASP.NET中,这意味着当前的请求上下文。并且一次只能有一个线程处于特定的上下文中。所以,会发生的是执行Index()
的线程在请求上下文中,在WaitAll()
中被阻止。另一方面,AsyncMethod()
的两个调用都试图在相同的上下文(完成下载后)之后恢复,但是他们无法这样做,因为Index()
仍然在该上下文中执行。因此,这些方法都在deadlock中,因此没有任何反应。
(同样的死锁也会在GUI应用程序中发生,因为GUI上下文在这方面表现相似。控制台应用程序没有这个问题,因为它们没有任何上下文。)
对此的修复是双重的:
永远不要同步等待async
方法。 (可能唯一的例外是如果想要从控制台应用程序的Main()
方法执行异步方法。)
相反,请异步等待它们。在您的情况下,这意味着使用await Task.WhenAll(a, b)
。
ConfigureAwait(false)
(即那些不需要在请求上下文中执行的方法)。使用1或2可以解决您的问题,但如果您同时执行这两项操作可能会更好。
有关此问题的更多信息,请阅读Stephen Cleary的文章Don't Block on Async Code。
答案 1 :(得分:0)
它的工作原理如下:
public Task<string> FakeAsyncMethod()
{
var link = "http://google.com";
var webclient = new WebClient();
var t = new Task<string>(() => webclient.DownloadString(new Uri(link)));
return t;
}
public async Task Index()
{
var a = FakeAsyncMethod();
var b = FakeAsyncMethod();
a.Start();
b.Start();
Task.WaitAll(a, b);
}
async void AsyncCall()
{
await Index();
}
我不知道为什么它不适用于您的方法,但我怀疑这是因为标有async
关键字的方法返回的任务是在运行状态下创建的(更确切地说,使用{{1}等于Status
)。我会更多地研究它。
编辑:另一种方法是使用与WaitingForActivation
关键字配对的Task.WhenAll
。
await