SvdSinner
SvdSinner

Reputation: 1048

Debugging async tasks that don't return

I'm working on a small WPF desktop utility and am using the async/await methodology to allow things to process in parallel.

However, I keep running into issues where an awaited async Task simply never returns. No exceptions are thrown, and if I pause the app in the debugger, the call stack says it is running "External code" called by the line where the async task is called. (Specifically, it hangs at: WindowsBase.dll!System.Windows.Threading.DispatcherSynchronizationContext.Wait(System.IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout) on my current example of this.) I've seen this several times and worked around it, but each time I have to add debugging code to figure out what line things stopped executing on. (The same code might execute fine for several iterations, and then hang) The same issue has happened both on awaiting my own async methods and on awaiting framework async methods E.G. Stream.CopyToAsync and Stream.ReadAsync)

Is there a way to look into executing Tasks in Visual Studio 2017? I tried opening the "Tasks" window, but I've never gotten anything but "No tasks to display" -- possibly I'm not using that window correctly?

FWIW, I'm doing a lot (hundreds) of concurrent background operations, but none overlap. Mostly web service calls, file system reads and MD5 checksum computations. Is the async/await limited in what it can do concurrently without freezing up? Or a maximum nesting of awaits?

Upvotes: 0

Views: 3966

Answers (1)

Kevin Gosse
Kevin Gosse

Reputation: 39007

This looks like a classic case of deadlock due to the synchronization context. This happens when you wait synchronously on a task returned by a method that internally calls await. For instance:

public void Deadlock()
{
    DoSomething.Wait();
}

public Task DoSomething()
{
    // Some stuff
    await DoSomethingElse();
    // More stuff
}

When inside of a synchronization context, the continuation of await is posted to that very same synchronization context. But the thread of the synchronization context is waiting on the DoSomething().Wait(), and therefore not available. We have a deadlock:

  • The continuation is waiting for the thread of the synchronization context to be available
  • The thread of the synchronization context is waiting for the task to be completed, which will happen when the continuation is executed

There are two possible fix:

  • Stop waiting synchronously on tasks
  • Use .ConfigureAwait(false) when awaiting, so that the continuation isn't posted to the synchronization context

Upvotes: 8

Related Questions