Question

I try to implement async/await methods to my code in order to let UI refresh.

My code is compose of few lazy loaded instantiation that are nested but could be created independently and in any order. I then added async/await on both. Using one at a time seems to give me proper results. But while I nest them, I have behavior that I never seen before. Calling a class constructor seems to force the first async/await to return immediately while my object is not yes initialized (constructor not yet called) - look like my code suddenly become async although I have an await ???

I'm stuck and completely lost. Any help will be really welcome.

Does nested await are permits and they are, is there any exception to them ?

Offending code (calling EmtpApplication property)

    // ******************************************************************
        [XmlIgnore]
        public EmtpApplication EmtpApplication
        {
            get
            {
                if (_emtpApplication == null)
                {
                        // LoadEmtpApplication();
                        LoadEmtpApplicationAsync();
                }
                return _emtpApplication;
            }
            set { _emtpApplication = value; }
        }

        // ******************************************************************
        public async void LoadEmtpApplicationAsync()
        {
// this is the second await (the nested one) - initial is not shown here
            await Task.Run(() => LoadEmtpApplication());
        }

        // ******************************************************************
        public void LoadEmtpApplication()
        {
            if (_emtpApplication == null)
            {
                try
                {
                    // Offending line. The static constructor and constructor are called but after I receive a NullReferenceException on my initial await (breakpoints to confirm)
                    // If no await (calling directly LoadEmtpApplication give me proper result) ???
                    _emtpApplication = new EmtpApplication(true);
                }
                catch(Exception ex)
                {
                    Debugger.Break();
                }
                if (_emtpApplication ==null)
                {
                    Debugger.Break();
                }
            }
        }   

For additional info here is the initial await:

// ******************************************************************
public async void LoadDesign(String path = null, bool throwExceptionOnError = false)
{
    await Task.Run(() => LoadDesignInternal(path, throwExceptionOnError)).ConfigureAwait(true);
}

public void LoadDesignInternal(String path = null, bool throwExceptionOnError = false)
// EO: "path" should be the BaseCase path but it will differ during simulation execution
{
    lock (_lockLoadingEmtpDesign)
    {
        if (path == null)
        {
            path = _path;
        }

        try
        {
            IsLoadingEmtpDesign = true;

            if (System.IO.File.Exists(path))
            {
                _emtpDesign = this.EmtpApplication.Load(path);
            }
        }

Sample for Dan:

async void TestNestedAwait()
{
    await Task.Run(()=>FirstAsyncCall());
    Debug.Print("and here after");

}

async void FirstAsyncCall()
{
    System.Threading.Thread.Sleep(10000);
    Debug.Print("here first");
Was it helpful?

Solution

Imagine your async method returned Task instead of void. Who waits for that Task to complete? Your property getter would need to wait for the Task to complete, though of course calling .Wait() on the returned Task would essentially make the call synchronous.

Consider removing your property (property getters that perform a lot of actions are generally a bad idea anyway) and use a method directly, somewhat like this:

        public async Task<EmptApplication> LoadEmtpApplicationAsync()
        {
            await Task.Run(() => LoadEmtpApplication());
            return _emptApplication;
        }

Now whatever consuming code can create it asynchronously in two steps:

  1. Call the method and store the Task. This begins the load process.
  2. When you reach the point where you need the EmptApplication instance, get the Result of the task to get the instance. This will block if the Load was not yet complete, but presumably you start the loading early enough that it won't block very long. Alternately, you can propagate the async/await pattern further upward and await the Task to get the instance.

An analogy that might help clarify what async/await really means:

Suppose Bob wants Alice the intern to prepare a report for him. They've nicknamed Alice 'Thread Pool' because everybody gives her stuff to do when they'd rather not do it themselves. He doesn't want to sit around all day waiting for the report, though, so he'd rather check in with Alice when he actually needs the report.

Alice, in turn, needs to run a program for a while to crunch the numbers for the report. While that's happening, she can do other stuff for other people. She sets up the program to send her a text message when it's done.

Bob finishes his stuff and waits for Alice to provide the report. Alice gets the text message, finishes up the report, then gives it to Bob.

public sealed class Bob
{
    private Alice _alice;

    public void DoJob()
    {
        //Have Alice produce the report
        var reportTask = _alice.ProduceReport();

        //Talk around the water cooler...

        //Okay, the report is due, so Bob waits by his desk for Alice to hand it over
        //If Alice was already done, she may have just left it on his desk
        var report = reportTask.Result;

        //Do something with the report...
    }
}

public sealed class Alice
{
    public async Task DoOtherStuff()
    {
        //Other stuff requested by other folks, broken up similarly into small chunks using await...
    }

    public async Task<Report> ProduceReport()
    {
        //Alice sets the task to run
        //She lets the caller (Bob) know that she's started, so he can continue with his work
        await Task.Run((Action)CrunchNumbers);

        //Alice gets the text message, then finishes up the report
        //She goes and hands it to Bob at his desk
        return new Report();
    }

    private void CrunchNumbers()
    {
        //Crunch, crunch...
    }
}

This all works because the Task return value from the async methods allows for interested parties to know when the tasks complete. Async methods that return 'void' on the other hand don't provide any notification. This would be like Bob asking Alice to produce the report and give it directly to his boss. He has no way to know when the report is delivered or if it ever gets delivered at all. In particular, if Alice gets in an accident (an Exception happens), the report won't arrive, but he won't find out until he notices the report never showed up.

In your original code, calling LoadEmtpApplicationAsync() is asking for the instance to be generated, but without any way to know when it's done. The code then immediately tries to grab the report without waiting for it to be complete, retrieving a null reference. LoadEmtpApplicationAsync() does queue up the Task to run on the thread pool and asks to be notified when the task is done, but it returns to the caller as soon as it has scheduled the Task. If it returned a Task itself, it could notify the caller when it was complete. Since it returns void, it literally does nothing as soon as it receives the call back that the task is complete, since there is no code after the await.

OTHER TIPS

I recommend you read my async intro and my MSDN article on async best practices. These will teach you to avoid async void.

Regarding properties, I have a separate blog post on that. Since you're specifically doing asynchronous lazy initialization, you can use an AsyncLazy<T> type originally developed by Stephen Toub and included in my AsyncEx library:

[XmlIgnore]
public AsyncLazy<EmtpApplication> EmtpApplication
{ get { return _emtpApplication; } }

private readonly AsyncLazy<EmtpApplication> _emtpApplication =
  new AsyncLazy<EmtpApplication>(() => new EmtpApplication(true));

Usage:

var application = await EmtpApplication;
var design = application.Load(path);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top