Task.WaitAll keeps in loop

怎甘沉沦 提交于 2020-05-29 09:47:25

问题


I'm trying this async code just for testing the async keyword:

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();
}

but when I debug it, the debugger hits the Task.WaitAll and just do nothing(the return keywork is never executed).. If I set await before the two 'AsyncMethod' and remove the Task.WaitAll it works.. So what am I doing wrong?


回答1:


Because your method looks like ASP.NET MVC controller action, I'm assuming you're running on ASP.NET.

By default, an async method resumes on the same context where it was suspended (i.e. where you called await). In ASP.NET, this means the current request context. And only one thread can be in a specific context at a time. So, what happens is that the thread that executes Index() is in the request context, blocked in WaitAll(). On the other hand, both invocations of AsyncMethod() are trying to resume on the same context (after they finished downloading), but they are unable to do so, because Index() is still executing in that context. Because of this, the methods are in a deadlock and so nothing happens.

(The same deadlock would also happen in an GUI application, because GUI context behave similarly in this regard. Console applications don't have this issue, because they don't have any context.)

The fix for this is twofold:

  1. Never wait synchronously for an async method. (Probably the sole exception is if want to execute an async method from the Main() method of a console application.)

    Instead, wait for them asynchronously. In your case, that means using await Task.WhenAll(a, b).

  2. Use ConfigureAwait(false) in your "library" methods (i.e. those that don't actully need to execute on the request context).

Using 1 or 2 would fix your issue, but it's probably best if you do both.

For some more information about this issue, read Stephen Cleary's article Don't Block on Async Code.




回答2:


It works this way:

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();
}

I don't know why it won't work with your method, but I suspect it's because tasks returned by methods marked with the async keyword are created in running state (more precisely, with Status equals to WaitingForActivation). I will look into it more.

EDIT: an alternative is to use Task.WhenAll paired with the await keyword.

public async Task Index()
{
    var a = AsyncMethod();
    var b = AsyncMethod();
    await Task.WhenAll(a, b);
}


来源:https://stackoverflow.com/questions/14046471/task-waitall-keeps-in-loop

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!