locked
Async await not returning RRS feed

  • Question

  • Hi,

    I have the following code:

    async internal Task<IEnumerable<PreparedScenes>> GetPreparedScenes()
            {
                var t = Task<IEnumerable<PreparedScenes>>.Factory.StartNew(() =>
                 {
                     var p = _context.prepared;
                     return p.ToList();
                 });
                // t.Wait();
                return await t;
            }

    When I try to run it, it never returns the value.  But if I uncomment the t.wait() line, the code works, but is not async anymore.  Is there something I am missing??

    Thanks,

    Eric jalbert

    Monday, January 30, 2017 9:38 AM

Answers

  • By default when you await on a task and the task completes the continuation has to be run on the thread that triggered the original call. In the case of a method called from the UI that is the UI thread. If the continuation doesn't actually need to run on a particular thread (a common case) then ConfigureAwait(false) tells the runtime that the continuation can be run on an arbitrary thread. In general that is what you want so the general recommendation is to do that.

    But there are some cases where you cannot/do not want to do that. This would include anything that touches the UI elements. It would also include anything that relies on thread-local storage.

    My gut instinct is that you may be deadlocking the UI thread (or whatever thread you are calling this method from). Here's my rationale. If you aren't using async/await then when the UI thread calls your method it is going to get a result back, that is a blocking call. Inside the method you call Task.Wait to wait for the results. Until the task completes your method is blocked as is the caller. When the task completes the thread continues.

    Now introduce the async/await. The UI thread will now get a task back. It has 2 choices, it can either await on the task or continue without waiting (the common case with a void event handler). But your async method will have a continuation that has to execute to return the results. Since you didn't specify ConfigureAwait(false) the continuation has to run on the UI thread. If that thread is busy (or blocked waiting on the results) then you deadlock. Here's a sample of what could cause it but I haven't tested it to confirm.

    async void SomeUIHandler (...)
    {
       //The UI thread is blocked waiting for this to return
       await DoWorkAsync();   
    }
    
    async Task DoWorkAsync()
    {
       //Running on UI thread
       await Task.Run(() => { ... });
    
       //No ConfigureAwait so back on UI thread but since the UI
       //thread is blocked waiting for the method to complete the continuation cannot run so we deadlock
       ...   
    }
    
    Google for "UI thread deadlock async" to get several blog articles about this issue.

    • Marked as answer by Eric Jalbert Tuesday, January 31, 2017 7:52 AM
    Tuesday, January 31, 2017 4:04 AM

All replies

  • It never returns a value or this method never returns at all? Stepping through your code, when you hit the await your method should immediately return to the caller. Until the task completes it'll never trigger the return statement. So if your method is never returning back to the caller then I'd say the issue is on the caller side. If the task is never completing then the issue is with your task lambda.

    Looking at the code I'm really curious about the _context field and whether it is thread safe or not.  I also notice you're not using ConfigureAwait which tells me this could be happening on a UI thread. If so then be aware that you can quite easily deadlock the UI thread if you're using UI handlers to trigger this process. There are plenty of blog articles on how this can get messed up.

    Additionally note that if your caller never waits on the task, any exception thrown within the task will never be raised such that you can see the error.

    Finally, I wouldn't bother using async/await at all here. You're creating a task and then awaiting on the result. There isn't any reason to that if you don't intend to do anything with the results. Simply return the original task and let the caller await as they are going to have to do that anyway.

    Michael Taylor
    http://www.michaeltaylorp3.net

    Monday, January 30, 2017 2:54 PM
  • in fact it never returns at all.  I did few tests like changing the return value for IEnumerable<int> and returning a new List<int>() and I get the same result: the program get stuck at the await line and waiting forever.  but if I uncomment the t.wait() line, the program is not stuck anymore.  

    Another test: return int rather than IEnumerable<int>.  this time the program did not blocked at any line.

    The only think I am trying to do is to create an async task that will get data from database without locking UI.  I am using EntityFramework.  I did similar things many times, but that's the first time I get a deadlock.  So I am searching what is different this time.  I never used ConfigureAwait, and my threads were working in the background as expected, can you tell me more about that?

    Thanks,

    Eric Jalbert

    Monday, January 30, 2017 10:59 PM
  • By default when you await on a task and the task completes the continuation has to be run on the thread that triggered the original call. In the case of a method called from the UI that is the UI thread. If the continuation doesn't actually need to run on a particular thread (a common case) then ConfigureAwait(false) tells the runtime that the continuation can be run on an arbitrary thread. In general that is what you want so the general recommendation is to do that.

    But there are some cases where you cannot/do not want to do that. This would include anything that touches the UI elements. It would also include anything that relies on thread-local storage.

    My gut instinct is that you may be deadlocking the UI thread (or whatever thread you are calling this method from). Here's my rationale. If you aren't using async/await then when the UI thread calls your method it is going to get a result back, that is a blocking call. Inside the method you call Task.Wait to wait for the results. Until the task completes your method is blocked as is the caller. When the task completes the thread continues.

    Now introduce the async/await. The UI thread will now get a task back. It has 2 choices, it can either await on the task or continue without waiting (the common case with a void event handler). But your async method will have a continuation that has to execute to return the results. Since you didn't specify ConfigureAwait(false) the continuation has to run on the UI thread. If that thread is busy (or blocked waiting on the results) then you deadlock. Here's a sample of what could cause it but I haven't tested it to confirm.

    async void SomeUIHandler (...)
    {
       //The UI thread is blocked waiting for this to return
       await DoWorkAsync();   
    }
    
    async Task DoWorkAsync()
    {
       //Running on UI thread
       await Task.Run(() => { ... });
    
       //No ConfigureAwait so back on UI thread but since the UI
       //thread is blocked waiting for the method to complete the continuation cannot run so we deadlock
       ...   
    }
    
    Google for "UI thread deadlock async" to get several blog articles about this issue.

    • Marked as answer by Eric Jalbert Tuesday, January 31, 2017 7:52 AM
    Tuesday, January 31, 2017 4:04 AM
  • This looks like what I was doing.  Thank you very much taking time to write this all, it is very clear and helpful.    

    Thanks,

    Eric Jalbert

    Tuesday, January 31, 2017 7:53 AM