【问题标题】:Form is displaying before async function completes在异步功能完成之前显示表单
【发布时间】:2017-04-01 11:59:22
【问题描述】:

我有一个扫描给定网络并返回有效 IP 地址的 WinForms 项目。找到所有地址后,我为每个地址创建一个用户控件并将其放置在表单上。我的 ping ip 地址函数使用 asyncTask 我认为在执行其他操作之前会“等待”执行,但事实并非如此。我的表单显示为空白,然后在 5 秒内,所有用户控件都出现在表单上。

声明:

private List<string> networkComputers = new List<string>();

这是 Form_Load 事件:

private async void MainForm_Load(object sender, EventArgs e)
{
    //Load network computers.
    await LoadNetworkComputers();
    LoadWidgets();
}

LoadNetworkComputers 函数在这里:

private async Task LoadNetworkComputers()
{
    try
    {
        if (SplashScreenManager.Default == null)
        {
            SplashScreenManager.ShowForm(this, typeof(LoadingForm), false, true, false);
            SplashScreenManager.Default.SetWaitFormCaption("Finding computers");
        }
        else
            Utilities.SetSplashFormText(SplashForm.SplashScreenCommand.SetLabel, "Scanning network for computers.  This may take several minutes...");

        networkComputers = await GetNetworkComputers();
    }
    catch (Exception e)
    {
        MessageBox.Show(e.Message + Environment.NewLine + e.InnerException);
    }
    finally
    {
        //Close "loading" window.
        SplashScreenManager.CloseForm(false);
    }
}

最后两个函数:

private async Task<List<string>> GetNetworkComputers()
{
    networkComputers.Clear();
    List<string> ipAddresses = new List<string>();
    List<string> computersFound = new List<string>();

    for (int i = StartIPRange; i <= EndIPRange; i++)
        ipAddresses.Add(IPBase + i.ToString());

    List<PingReply> replies = await PingAsync(ipAddresses);

    foreach(var reply in replies)
    {
        if (reply.Status == IPStatus.Success)
            computersFound.Add(reply.Address.ToString());
    }

    return computersFound;
}

private async Task<List<PingReply>> PingAsync(List<string> theListOfIPs)
{
    var tasks = theListOfIPs.Select(ip => new Ping().SendPingAsync(ip, 2000));
    var results = await Task.WhenAll(tasks);

    return results.ToList();
}

我真的很困惑为什么在MainForm_Load 事件中的代码完成之前显示表单。

编辑 我忘了提到在LoadNetworkComputers 中它加载了一个启动表单,让用户知道应用程序正在运行。当表格出现在其后面时,我试图避免。这是一个屏幕截图(敏感信息已被涂黑):

【问题讨论】:

  • 也许我遗漏了一些东西,但 async/await 的全部目的并不是让您的代码在进行长时间操作时继续执行?

标签: c# multithreading thread-safety async-await task


【解决方案1】:

我的表单显示为空白,然后在 5 秒内,所有用户控件都出现在表单上。

这是设计使然。当 UI 框架要求您的应用显示表单时,它必须立即这样做。

要解决此问题,您需要确定在进行async 工作时希望您的应用看起来像什么,在启动时初始化为该状态,然后 async 工作完成时更新 UI。微调器和加载页面是常见的选择。

【讨论】:

    【解决方案2】:

    使用 async-await 的原因是让函数的调用者能够在您的函数必须等待某事时继续执行代码。

    好处是即使等待功能没有完成,这也会让你的 UI 保持响应。例如,如果你有一个按钮可以LoadNetworkComputersLoadWidgets,你会很高兴在这个相对较长的操作期间你的窗口仍然会被重新绘制。

    由于您已将 Mainform_Load 定义为异步,因此您已表示希望 UI 继续运行,而无需等待 LoadNetWorkComputers 的结果。

    In this interview with Eric Lippert(在中间搜索 async-await) async-await 与做饭的厨师进行比较。每当厨师发现他必须等待面包烤好时,他就会开始四处张望,看看他是否可以做其他事情,然后开始做。烤了一会儿面包,他继续准备烤面包。

    通过保持表单加载异步,您的表单能够显示自己,甚至显示正在加载网络计算机的指示。

    更好的方法是创建一个简单的启动对话框,通知操作员程序正忙于加载网络计算机。此启动对话框的异步表单加载可以执行操作并在完成后关闭表单。

    public class MyStartupForm
    {
        public List<string> LoadedNetworkComputers {get; private set;}
    
        private async OnFormLoad()
        {
            // start doing the things async.
            // keep the UI responsive so it can inform the operator
            var taskLoadComputers = LoadNetworkComputers();
            var taskLoadWidgets = LoadWidgets();
    
            // while loading the Computers and Widgets: inform the operator
            // what the program is doing:
            this.InformOperator();
    
            // Now I have nothing to do, so let's await for both tasks to complete
            await Task.WhenAll(new Task[] {taskLoadComputers, taskLoadWidgets});
    
            // remember the result of loading the network computers:
            this.LoadedNetworkComputers = taskLoadComputers.Result;
    
            // Close myself; my creator will continue:
            this.Close();
        }
    }
    

    还有你的主要形式:

    private void MainForm_Load(object sender, EventArgs e)
    {
        // show the startup form to load the network computers and the widgets
        // while loading the operator is informed
        // the form closes itself when done
        using (var form = new MyStartupForm())
        {
            form.ShowDialog(this);
    
            // fetch the loadedNetworkComputers from the form
            var loadedNetworkComputers = form.LoadedNetworkComputers;
            this.Process(loadedNetworkComputers);
        }
    }
    

    现在在加载时,在加载项目时会显示 StartupForm 而不是主窗体。操作员会被告知主窗体尚未显示的原因。加载完成后,StartupForm 会自行关闭并继续加载主窗体

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-13
      • 2022-01-05
      • 1970-01-01
      • 1970-01-01
      • 2016-07-16
      • 1970-01-01
      • 2018-10-18
      • 2016-06-28
      相关资源
      最近更新 更多