Wait() or Result in async/await methods are never actually return and make the app to hang forever

I really love using async, I tried with async/await operators and it works like a charm.

What happens though when you just return a Task<T> and Wait() to finish because you want to get the result synchronously.
Asynchronous is nice but I will have a couple of situations where I want to use the same methods synchronous.

For example, this will never get to the Console.WriteLine()

private void PrintFileToConsole()
        {
            FileRepositoy fileRepo = new FileRepositoy ();
            string contents= fileRepo.Read(Path.Combine("Directory", "file.dat")).Result;
            Console.WriteLine ("File contents: {0}", contents== string.Empty ? "No contents" : contents);
        }

But this will work just fine

private async void PrintFileToConsole()
        {
            FileRepositoy fileRepo = new FileRepositoy ();
            string contents= await fileRepo.Read (Path.Combine ("Directory", "file.dat"));
            Console.WriteLine ("Last Exception: {0}", contents== string.Empty ? "No contents" : contents);
        }

Is this a known issue?

Posts

  • ChWoChWo DEMember

    I think you have to start() your task fist before you wait() for the result.

  • No, I don't think I should, something strange going on in general.

    OK, say you have this simple code.

    public async Task<string> Read (string filePath)
            {
                return await Task.Factory.StartNew (() =>
                {
                    using (var storage = IsolatedStorageFile.GetUserStoreForApplication()) {
                        string result = string.Empty;
    
                        if (!storage.FileExists (filePath))
            ``              return result;
    
                        using (var fileStream = storage.OpenFile(filePath, FileMode.Open)) {
                            using (StreamReader sr = new StreamReader(fileStream)) {
                                return result = sr.ReadToEnd ();
                            }
                        }
                    }
                });
            }
    

    I already using the Task.Factory.StartNew() so there is no need to Start() the Task

    Now, if you try to Wait() or grab the Resultproperty to make the code synchronous and freeze until you have that string content the application will hang forever!

    Though i tested calling Start() anyway or using Task.Run() and starting it.
    In that case you will get the desired result, but you will get also the follow exception.

    Some code for example.

    Task<string> readFileTask= fileRepo.Read(BugSenseProperties.FolderName + "\\bugsense.test");
    readFileTask.Start ();
    string contents = readFileTask.Result;
    

    Try the above code and tell me what you get.
    If I use synchronous methods, everything's working fine.

    The exception that is thrown is

    System.InvalidOperationException: The task has already completed
    at at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.SetResult (string) <0x000c4>
    at BugSense.Xamarin.Core.FileRepository/d__9.MoveNext () [0x000d4] in d:\Visual Studio Projects\Bugsense\Xamarin\BugSense.Xamarin.Core\File.IO\FileRepository.cs:88
    at at (wrapper unbox) BugSense.Xamarin.Core.FileRepository/d__9.MoveNext ()
    at at System.Threading.Tasks.SynchronizationContextContinuation.m__36 (object)
    at Android.App.SyncContext/c__AnonStorey2E.<>m__1F () [0x00000] in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.8.0-branch/b76e8ec4/source/monodroid/src/Mono.Android/src/Android.App/SyncContext.cs:23
    at Java.Lang.Thread/RunnableImplementor.Run () [0x0000b] in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.8.0-branch/b76e8ec4/source/monodroid/src/Mono.Android/src/Java.Lang/Thread.cs:32
    at Java.Lang.IRunnableInvoker.n_Run (intptr,intptr) [0x00008] in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.8.0-branch/b76e8ec4/source/monodroid/src/Mono.Android/platforms/android-17/src/generated/Java.Lang.IRunnable.cs:71
    at at (wrapper dynamic-method) object.7154d119-5116-45ca-a195-0a5d0e696467 (intptr,intptr)
    at

    I know it has already completeted, cause it was already started, but if you replace the Start() with a Wait(), let me know what you get. And I hope it's not different than my behavior :)

    Thank you for your reply.

  • SKallSKall USMember ✭✭✭✭
    edited August 2013

    Why would you not create separate methods?

    public string Read(string filePath)
    {
      ...
    }
    
    public async Task<string> Read (string filePath)
    {
       return await Task.Factory.StartNew (() =>
       {
          return Read(filePath);
       });
    }
    
  • Yeah OK, that is good, I'm just focusing on the problem right now, you think this is a solution to the problem?

    I can easily solve this like that, which already did, but the problem exists, I just need to know if this is something that the guys at Xamarin are aware, something I do wrong etc.

    Other than that, I just can do what I did and move on like I said that I did, but it keeps bugging me to know that this is not working as it "should" or I'm waiting too Microsoft like behavior :)

    Thank you for your comment.

  • ChWoChWo DEMember

    Maybe there is a different beahviour between Android and Windows. I tried your code inside a console application and it's working fine.

    By the way: When you use a StreamReader inside an async method you'd better use ReadToEndAsync() instead of ReadToEnd().

    By the way (part II): Don't try to put async methods into a synchronous behaviour. It may works on Android or iOS but when you try to use the same code on WindowsPhone you are going to freeze your app. Some async methods never return until the calling thread has been released. That's how Microsoft forces app developers to make responsive UIs.

  • Thank you for your comments Ch Wo, I really understand what you say and aware about this, I do lot of Microsoft programming, I just want to figure out the behavior in my current context.

    I understand that I shouldn't create something async to use it as synchronous, it's bad practice, but it's there and I was wondering about it.

    I'll stick with my current implementation calling the sync method from any Task operations that I might need.

    DaveHunt, thank you for the link, will have a review.

    Regards.

  • DaveHunt is right, deadlocks is what I was facing because I mix synchronous code wrapped to partially asynchronous methods.

    I'll need to separate implementations to be 100% correct, and feel good with its functionality/

    Thank you.

  • EugeneEugene USMember

    Probably something like this can help

    public static Task<T> WaitAsync<T>(this Task<T> task)
    {
        // Ensure that awaits were called with .ConfigureAwait(false)
    
        var wait = new ManualResetEventSlim(false);
    
        var continuation = task.ContinueWith(_ =>
        {
            wait.Set();
            return _.Result;
        });
    
        wait.Wait();
    
        return continuation;
    }
    
  • I did implemented something like that, ManualResetEventSlim is the most appropriate and effective class you could use to such a case.

    But then decided to uses an alternative which creates a Task and the wait for it which resolves any deadlocks.

    Something like it would be,

    Task task = Task.Run(async () => await Do());
    task.Wait();
    

    The above solved my problems but the ManualReseEventSlim approach could be better and more appropriate so you don't have to create a new Task.

    Thinking of using it to sync web service APM request API to wait for it.

  • JakeBjorkeJakeBjorke USMember
    edited September 2013

    One way that I have done it to implement something async synchronously has been to use the Func class. I have not tested it on any of my Xamarin code but it does work with Windows Phone 8 and Store.

    Func<Task<T>> someTask = async () =>
    {
         var something = await GetSomethingAsync();
         return something;
    }
    someTask().Wait();
    return someTask().Result;
    

    Pretty similar to what you have done.

  • Thank you for your time and contribution.

    It looks good, and without starting another Task.

    Maybe I'll try it.

  • AlexPalmaAlexPalma USMember ✭✭

    @JakeBjorke, for something like that you don't need all that work, the following will achieve the same.

    return GetSomethingAsync().Result;
    

    Now you need to remember that if your running on the UI thread this will block the UI thread, another thing to remember is deadlocks that where already discussed here, so to prevent deadlocks and since this is not async code all the way, inside your GetSomethingAsync method where you have await you will need to call the ConfigureAwait(false), so that you don't have deadlocks.

    private async Task<int> GetSomethingAsync()
    {
        return await Task.Run(() => 1).ConfigureAwait(false);
    }
    
  • Yes, deadlocks are the most important.

    I use ConfigureAwait(false) everywhere now.
    I didn't know that changing the context is resolving the deadlock problem.

    Thank you.

  • AlexPalmaAlexPalma USMember ✭✭
    edited September 2013

    @gtas, but you also need to be careful with the ConfigureAwait(false), since if you have multiple in your method that may not behave the way you are expecting, since the code after the 2nd await with ConfigureAwait(false), may or may not run on the original context of your async method.
    In this case you better just create an additional method that will call the async method and uses the ConfigureAwait(false);

    private void MyNotAsyncMethod()
    {
        MyMethodCallForNotAsync().Wait();
        //this can also work with lambda in that case we don't need the additional method.
        Func<Task> task = async () => { await MyMethodAsync().ConfigureAwait(false); };
        task().Wait();
    }
    
    private async Task MyMethodCallForNotAsync()
    {
        await MyMethodAsync().ConfigureAwait(false);
    }
    
    private async Task MyMethodAsync()
    {
        //do some work
        await CallMethodAsync1();
        //do aditional work
        await CallMethodAsync2();
        //do more work or not.
    }
    

    I know is 1 more method to create but that is the price to pay for not going full async, but better create 1 additional method and everything works the way is supposed.

  • I understand, it's how already implemented stuff :)

    And in the library, it doesn't matter to return in the UI context, so if the call return back to a UI context it will continue running to the caller context any way.

    But your point is right.

    Thank you.

Sign In or Register to comment.