Task.WaitAll保持循环

Mur*_*nze 2 c# async-await

我正在尝试这个异步代码只是为了测试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();
}
Run Code Online (Sandbox Code Playgroud)

但是当我调试它时,调试器命中Task.WaitAll并且什么都不做(返回键工作永远不会执行)..如果我在两个'AsyncMethod'之前设置等待并删除Task.WaitAll它工作..那么我做错了什么?

svi*_*ick 9

因为您的方法看起来像ASP.NET MVC控制器操作,我假设您正在运行ASP.NET.

默认情况下,异步方法在挂起的相同上下文(即您调用的位置await)上恢复.在ASP.NET中,这意味着当前的请求上下文.并且一次只能有一个线程处于特定的上下文中.所以,会发生的是执行的线程Index()在请求上下文中被阻塞WaitAll().另一方面,两个调用AsyncMethod()都试图在相同的上下文(在完成下载之后)恢复,但是他们无法这样做,因为Index()仍然在该上下文中执行.因此,这些方法处于死锁状态,因此没有任何反应.

(同样的死锁也会在GUI应用程序中发生,因为GUI上下文在这方面表现相似.控制台应用程序没有这个问题,因为它们没有任何上下文.)

解决这个问题有两个方面:

  1. 永远不要同步等待async方法.(可能唯一的例外是如果想从Main()控制台应用程序的方法执行异步方法.)

    相反,等待它们异步.在您的情况下,这意味着使用await Task.WhenAll(a, b).

  2. 使用ConfigureAwait(false)在您的"库"方法(即那些不actully需要对请求的上下文中执行).

使用1或2可以解决您的问题,但如果您同时执行这两项操作,则最好.

有关此问题的更多信息,请阅读Stephen Cleary的文章Do not Block on Async Code.