Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I have an application that connects to a REST API using async methods. I have this set up using async/await pretty much everywhere that connects to the API, however I have a question and some strange behavior that I don't completely understand. What I want to do is simply return a license in certain scenarios when the program shuts down. This is initiated by a window closing event; the event handler is as follows:

async void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            ...other synchronous code...

            //Check for floating licensing
            if (KMApplication.License != null && KMApplication.License.Scope != Enums.LicenseScope.Standalone)
            {
                for (int i = 0; i < 3; i++)
                {
                    try
                    {

                        await KMApplication.License.ShutDown(KMApplication.Settings == null
                                                                 ? Enums.LicenseReturnModes.PromptOnShutdown
                                                                 : KMApplication.Settings.LicenseReturnMode)
                                           .ConfigureAwait(false);
                        break;
                    }
                    catch (Exception ex)
                    {
                        _logger.Warn("Exception in license release, attempt " + i, ex);
                    }
                }
            }

            await KMApplication.ApiService.Disconnect().ConfigureAwait(false);

            _logger.Info("Shutdown Complete");

            Application.Current?.Shutdown();
        }

When this runs I can step through in the debugger and it gets to the first license shutdown call which is the first async awaited call. Then when I press F10 to step to the next line of code it just shuts down and is gone. I verified that the license release that is supposed to be happening in that line is in face happening so it appears to run to completion of that line but then shuts down or crashes or something. I also looked at the logs and it never gets to the Shutdown Complete line and I don't believe it's getting to the ApiService.Disconnect either.

I also tried running this as a sync method using Task.Run(() => ...the method...).GetAwaiter().GetResult() but that just deadlocks on the first call.

How do I handle this and have it run the async release, wait for it to be done, then shut down?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
121 views
Welcome To Ask or Share your Answers For Others

1 Answer

The fundamental problem in what you're trying to do is that async/await assumes the main application thread continues running. This assumption directly conflicts with the shutdown action, whose job is to terminate all running tasks.

If you examine the documentation on Window_Closing, it states the following (and only the following):

Occurs directly after Close() is called, and can be handled to cancel window closure.

This is important. The only thing this is supposed to do is allow you to programmatically cancel the window closure, thus prompting some additional user action.

Your expectations are befuddled because of how async/await works. Async/await appears to run in a linear fashion; however, what actually happens is that control is passed back to the caller at the first await. The framework assumes at that point that you do not wish to cancel the form close, and the program is allowed to terminate, taking all other actions with it.

Fundamentally, all C-style programs have a main entry point, which runs a loop. It's been that way since the early days of C, and continues that way in WPF. However, in WPF, Microsoft got a bit clever, and decided to hide this from the programmer. There are a couple of options to deal with things that need to happen after main window closing:

  1. Re-hijack the main loop from your program, and put the code there. The details on how to do this may be found here.

  2. Set an explicit shutdown mode, and kick off the task to initiate that. Call Application.Shutdown() as the very last line of code you need to execute.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...