8

I have a method that is defined as

public async Task SomeAsyncMethod()
{
   DoSomeStuff();
   await Task.Run(() => {
     DoSomeSyncStuff();
     DoSomeOtherSyncStuff();
   });
   var someDebugVariable = "someDebugValue";
}

The method itself does exactly what it is supposed to do and everything runs fine. Yet ... it looks like the "outer" async Task never completes.

Example: When I call it like this

public void CallerMethod()
{
   Task t = SomeAsyncMethod();
   t.Wait();
}

the t.Wait() never completes. Furthermore: if I place a breakpoint at the assignment of someDebugVariable it never gets hit.

I might add that DoSomeSyncStuff and DoSomeOtherSyncStuff really do what they are supposed to and debugging through them tells me that they both complete.

To prove my point I modified my method like this, and the results are still the same.

public async Task SomeAsyncMethod()
{
   DoSomeStuff();
   await Task.Run(() => {
     /*
     DoSomeSyncStuff();
     DoSomeOtherSyncStuff();
     */
     var a = 2; var b = 3; var c = a + b;
   });
   var someDebugVariable = "someDebugValue";
}

EDIT

I have tried removing everything but the await Task.Run and it does not change anything. It still does not complete.

The application is a WPF application. The caller thread is the UI thread.

What am I missing here?

Hemisphera
  • 816
  • 6
  • 23
  • Do you invoke it in single-threaded `SynchronizationContext`? – user4003407 Jan 25 '16 at 14:42
  • In your example, the task is never actually started, so `Wait` will never be called. – Daniel Kelley Jan 25 '16 at 14:42
  • If the task really would never be started, the anonymous method I passed would not start either, and therefore I would not be able to debug it, but I am. The program steps into it. – Hemisphera Jan 25 '16 at 14:46
  • @PetSerAl: I do not use any `SynchronizationContext`. The invokation is just a test-method I wrote to check if the task really does not complete. – Hemisphera Jan 25 '16 at 14:46
  • Can you comment DoSomeStuff(); in your last test code to ensure that problem is not in in this function? – serhiyb Jan 25 '16 at 14:46
  • What kind of application is this? Is the code run on a UI thread or an ASP Request context thread? – sara Jan 25 '16 at 14:48
  • Is it possible that `var someDebugVariable = "someDebugValue";` maybe optimized out by the compiler? Don't know if this should happen in debug version/mode, too. You might try to output `someDebugVariable` on the next line. – René Vogt Jan 25 '16 at 14:49
  • It is not optimized out by the compiler. I'm pretty sure the problem is a deadlock since Task.Run spawns a new thread that is unable to return control to the blocked main (UI or ASP) thread. Need more info to be sure though – sara Jan 25 '16 at 14:50
  • 1
    http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html this should be relevant for you – sara Jan 25 '16 at 14:56
  • @Hemisphera WPF UI thread use `DispatcherSynchronizationContext`, so you are actually use some `SynchronizationContext`. – user4003407 Jan 25 '16 at 14:58
  • Are there any blocking calls used in your sync methods called in your task? – sara Jan 25 '16 at 15:00
  • Possible duplicate of [An async/await example that causes a deadlock](http://stackoverflow.com/questions/15021304/an-async-await-example-that-causes-a-deadlock) – user4003407 Jan 25 '16 at 15:04
  • @PetSerEl: What I meant is that I never *explicitly* used one and never thought about it. The problem infact is a deadlock. I now need to find out where and why exactly but Steve's anwser solved my problem. – Hemisphera Jan 25 '16 at 15:05
  • @kai My sync methods make SQL-Server calls. – Hemisphera Jan 25 '16 at 15:05
  • That would explain it. I take it these are not made via a true asynchronous API? – sara Jan 25 '16 at 15:16
  • @kai: yes they do. But the actual `System.Data.SqlClient` calls are so deep down, hidden in other libraries (not all mine) and whatnot that I cannot always use true async/await. A real pity. – Hemisphera Jan 25 '16 at 15:18

1 Answers1

16

The t.Wait() call is causing a deadlock, and also makes the async call entirely pointless. I believe if you change the code to

await Task.Run(() => {
// ...
}).ConfigureAwait(false);

You can fix the deadlock and let the code proceed, but you should really get rid of the t.Wait() call. Anything that needs to be done with the results of the sync function calls should be done after the awaited task, not after the call of the async function.

More in depth: task.Wait() will block all execution on the main thread while the task is running. When the await task completes, it tries to marshall back to the main thread, but the main thread is blocked! Since A is waiting for B, and B is waiting for A, you get a deadlock.

See: http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

Steve
  • 571
  • 8
  • 16
  • Actually the `t.Wait` is just testing code. The caller is not async and I need to execute an async-method and read it's result and return it. I did not think about deadlocks. Will try to check that ... – Hemisphera Jan 25 '16 at 14:55
  • @serhiyb See the source I posted. He used the term deadlock, so I did as well. Main thread is waiting for task to complete, task is waiting for main thread. Deadlock. – Steve Jan 25 '16 at 15:00
  • This solved my problem. Now I just need to find the root of evil (= deadlock) and find out who blocks whom. – Hemisphera Jan 25 '16 at 15:06
  • 1
    @Hemisphera He already told you. The `await` is waiting for the UI thread to become free before it completes and the `.Wait()` is blocking the UI thread till the `await` completes. You have two things waiting for the other to finish before they stop waiting. – Scott Chamberlain Jan 25 '16 at 15:20
  • Dear Lord in heaven ... .ConfigureAwait(false) saved the day for me too. I had two awaits and the 2nd was never resolving/completing - this fixed it! Thanks. – Christopher Mar 21 '18 at 10:36
  • The old solutions are sometimes still the best. Hello from the future! – Kinetic Mar 10 '23 at 11:14