I don't quite understand the difference between Task.Wait and await.
I have something similar to the following functions in a ASP.NET WebAPI service:
public class TestController : ApiController
{
public static async Task<string> Foo()
{
await Task.Delay(1).ConfigureAwait(false);
return "";
}
public async static Task<string> Bar()
{
return await Foo();
}
public async static Task<string> Ros()
{
return await Bar();
}
// GET api/test
public IEnumerable<string> Get()
{
Task.WaitAll(Enumerable.Range(0, 10).Select(x => Ros()).ToArray());
return new string[] { "value1", "value2" }; // This will never execute
}
}
Where Get will deadlock.
What could cause this? Why doesn't this cause a problem when I use a blocking wait rather than await Task.Delay?
Task.Delay(1).Wait()which is good enough.Task.Delay(1).Wait()is basically the exact same thing asThread.Sleep(1000). In actual production code it is rarely appropriate.WaitAllis causing the deadlock. See the link to my blog in my answer for more details. You should useawait Task.WhenAllinstead.ConfigureAwait(false)a single call toBarorRoswon't deadlock, but because you have an enumerable that is creating more than one and then waiting on all of those, the first bar will deadlock the second. If youawait Task.WhenAllinstead of waiting on all of the tasks, so that you don't block the ASP context, you'll see the method return normally..ConfigureAwait(false)all the way up the tree until you block, that way nothing is ever trying to get back to the main context; that would work. Another option would be to spin up an inner synchronization context. Link. If you put theTask.WhenAllin anAsyncPump.Runit will effectively block on the whole thing without you needing toConfigureAwaitanywhere, but that's probably an overly-complex solution.