简体   繁体   中英

await Task.WhenAll automatically close the program

I have a method that doing Chromium browser initialization. After running two asynchronous method, the program automatically close. Did I use the Task.WhenAll wrong?

Here is the entry point of the program:

Start.cs

static class Start
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Initializer.Start();
        }
    }

Initializer.cs

public static class Initializer
    {
        public static async void Start()
        {
            // The task that automatically close the program.
            Task<bool> chromiumInitTask = ChromiumInfo.InitializeAsync();
            await chromiumInitTask;

            Task webviewInitTask = WebViewInfo.InitializeAsync();

            Task guiInitTask = GUIInfo.InitializeAsync();

            HardwareManager.Initialize();

            await webviewInitTask;

            await guiInitTask;

            GUIInfo.Layout.ChangeMainDisplay(ChromiumInfo.Browser);
            Application.Run(GUIInfo.Layout.GetLayoutForm());
        }
    }

ChromiumInfo.cs

public static class ChromiumInfo
    {
        private static CefSettings _settings;
        private static ChromiumWebBrowser _browser;
        private static BrowserSettings _browserSettings;
        private static Dictionary<string, bool> _initTasks = new Dictionary<string, bool>()
        {
            { "Settings", false },
            { "Browser Settings", false },
            { "Browser", false },
            { "Cef", false }
        };
        private static KeyboardHandler _keyboardHandler;

        /// <summary>
        /// An setting which using on initialize.
        /// </summary>
        public static CefSettings Setting => _settings;

        /// <summary>
        /// Representing a browser which can be show in UI.
        /// </summary>
        public static ChromiumWebBrowser Browser => _browser;

        /// <summary>
        /// A keyboard handler which handle various keyboard events.
        /// </summary>
        public static KeyboardHandler KeyboardHandler => _keyboardHandler;

        /// <summary>
        /// Occur when request to change the browser.
        /// </summary>
        public static event RequestChangeBrowserEventHandler RequestChangeBrowser;

        /// <summary>
        /// Initialize all Chromium components in asynchronously.
        /// </summary>
        /// <param name="initAllScripts">Indicate should initialize all scripts contain in the root script directory.</param>
        /// <returns>Return a task can be awaited. True means sucess. Otherwise, return false.</returns>
        public static async Task<bool> InitializeAsync(bool initAllScripts = true)
        {
            Task settingInit = SettingsInitializeAsync();
            Task browserSettingInit = BrowserSettingsInitializeAsync();
            // The below line that automatically close the program.
            await Task.WhenAll(settingInit, browserSettingInit);
            
            Task cefInit = Cef.InitializeAsync(_settings);
            await cefInit;
            _initTasks["Cef"] = true;

            Task browserInit = BrowserInitializeAsync();
            await browserInit;

            Task eventInit = EventInitializeAsync();
            await eventInit;

            Task scriptInit = ScriptInitializeAsync();
            await scriptInit;

            return _initTasks.Values.Where(it => it).Count() == _initTasks.Count;
        }

        private static async Task SettingsInitializeAsync()
        {
            try
            {
                _settings = new CefSettings();

                _settings.CommandLineArgsDisabled = false;
                _settings.CefCommandLineArgs.Clear();
                _settings.CefCommandLineArgs.Add("enable-3d-apis", "1");
                _settings.CefCommandLineArgs.Add("enable-webgl-draft-extensions", "1");
                _settings.CefCommandLineArgs.Add("enable-gpu", "1");
                _settings.CefCommandLineArgs.Add("enable-webgl", "1");
                _settings.CefCommandLineArgs.Add("gpu_compositing", "1");
                _settings.CefCommandLineArgs.Add("ignore-gpu-blocklist", "1");

                await Task.Delay(1000).ConfigureAwait(false);
                _initTasks["Settings"] = true;
            }
            catch (Exception e)
            {
                SystemLog.Write(e);
            }
        }

        private static async Task BrowserSettingsInitializeAsync()
        {
            try
            {
                _browserSettings = new BrowserSettings();
                _browserSettings.WebGl = CefState.Enabled;
                await Task.Delay(1000).ConfigureAwait(false);

                _initTasks["Browser Settings"] = true;
            }
            catch (Exception e)
            {
                SystemLog.Write(e);
            }
        }

        private static async Task BrowserInitializeAsync()
        {
            try
            {
                _browser = new ChromiumWebBrowser(Properties.Settings.Default.DefaultURL);
                _browser.BrowserSettings = _browserSettings;
                _browser.Dock = System.Windows.Forms.DockStyle.Fill;
                await Task.Delay(1000).ConfigureAwait(false);

                _initTasks["Browser"] = true;
            }
            catch (Exception e)
            {
                SystemLog.Write(e);
            }
        }

        private static async Task EventInitializeAsync()
        {
            KeyboardHandler keyboardHandler = new KeyboardHandler();
            WebCommandHandler commandHandler = new WebCommandHandler();
            _browser.ConsoleMessage += keyboardHandler.Handle;
            _browser.ConsoleMessage += commandHandler.Handle;
            _browser.AddressChanged += Custom_AddressChanged;
            _keyboardHandler = keyboardHandler;
            await Task.Delay(1000).ConfigureAwait(false);
        }

        private static async Task ScriptInitializeAsync()
        {
            string scriptPath = $@"{ProgramInfo.RootPath}\scripts";
            if (Directory.Exists(scriptPath))
            {
                var files = Directory.GetFiles(scriptPath, "*.js");
                files?.ToList().ForEach(f => _browser.GetMainFrame().ExecuteJavaScriptAsync(f, _browser.Address));
            }

            await Task.Delay(1000).ConfigureAwait(false);
        }

        private static void Custom_AddressChanged(object sender, AddressChangedEventArgs e)
        {
            var wv2SiteCount = Properties.Settings.Default.WebToWV2.Cast<string>()
                .Where(s => s.IndexOf(e.Address) >= 0).Count();

            if (wv2SiteCount > 0)
            {
                WebViewInfo.Navigate(e.Address);
                RequestChangeBrowser?.Invoke(null, new RequestChangeBrowserEventArgs(WebViewInfo.Browser));
            }
        }
    }

This is because of how await works. When await acts on a Task that is incomplete, it returns . So your program is working like this:

  1. Main runs, and calls Initializer.Start()
  2. Initializer.Start() runs and calls ChromiumInfo.InitializeAsync()
  3. ChromiumInfo.InitializeAsync() runs until it calls await Task.WhenAll(settingInit, browserSettingInit)
  4. Because Task.WhenAll returns an incomplete Task , ChromiumInfo.InitializeAsync() returns its own incomplete Task and execution returns to Initializer.Start() .
  5. The await in Initializer.Start() sees the incomplete Task and returns its own incomplete Task and execution returns to Main() .
  6. Because Main() doesn't act on the Task returned by Initializer.Start() , execution continues to the next line, which is the end of the program.

The solution is fairly simple: Change your Main method to async and use await .

public static async Task Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    await Initializer.Start();
}

The ability to make Main() async is a feature introduced in C# 7.1 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM