我正在尝试此异步代码,仅用于测试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却什么也不做(返回键永远不会执行)。
如果我在两个'AsyncMethod'之前设置await并删除Task.WaitAll,则它起作用。.那么我在做什么错呢?

最佳答案

因为您的方法看起来像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

关于c# - Task.WaitAll保持循环,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/14046471/

10-13 06:58