【问题标题】:c# winforms my ui still freeze with async in event loadc# winforms 我的 ui 仍然在事件加载中异步冻结
【发布时间】:2018-11-15 15:21:25
【问题描述】:

在我的表单事件加载中,我调用了一个方法loadDg:

private void form_Load(object sender, EventArgs e)
{
  loadDg();
}

private async Task loadDg()
        {
            pictureLoading.Visible = true;
            await Task.Run(() => { string[] datas = db.row("select * from products");
string[] datas2 = db.row("select * from users");
double one = Convert.ToInt32(datas[0]);
label1.Text = one.toString();
//....
});
pictureLoading.Visible = false; //hide gif animation 
}

在我的代码中,db.row 这个方法总是只返回 1 行(字符串数组),但我的 ui 仍然冻结,我尝试使用异步连续更新 UI 而不会在启动时冻结

【问题讨论】:

  • label1.Text = ... 在非 ui 线程中不好。
  • @Sinatr 修复它的最佳方法是什么?
  • @Flydog57 loadDg 不是“同步调用”。鉴于所显示的内容,它是一个异步方法,在显示的代码中完成之前没有任何内容是显式阻塞的,因此它将异步运行。将form_Load 标记为async 仅在您需要对异步方法的完成做出响应时才有用。在这种情况下,完成后什么都不做。
  • 要修复label1.Text =... 问题,创建一个设置标签的小函数并使用Control.Invoke 在拥有该控件的线程上调用该函数(请记住,Form 类是一个子类控制类)。有很多关于如何做到这一点的例子。

标签: c# .net winforms asynchronous async-await


【解决方案1】:

没有什么可以阻止您的代码异步运行。 pictureLoading 将在任务完成之前不可见。您应该像这样修复跨线程问题和 UI 逻辑:

private void form_Load(object sender, EventArgs e)
{
  pictureLoading.Visible = true;
  loadDg();
}


private async Task loadDg()
{

    await Task.Run(() =>
    {
        string[] datas = db.row("select * from products");
        string[] datas2 = db.row("select * from users");
        double one = Convert.ToInt32(datas[0]);

        label1.BeginInvoke((Action)delegate ()
        {
            label1.Text = one.toString();
            //hide gif animation 
            pictureLoading.Visible = false;
        });
        //....
    });

}

【讨论】:

  • 从线程池线程调用UI线程并立即返回UI线程有什么好处?
  • @PauloMorgado 那么对于这个简单的案例,您的解决方案会更好。但是,如果任务是一个循环,并且您想多次更新 UI,那么上面的代码就可以了(但在后一种情况下,有时BackgroundWorker 也可以工作)。
  • IProgress。但是,在这种情况下,您不会隐藏pictureLoading。你愿意吗?
  • @PauloMorgado 1.我试图通过对代码的最小更改来解决问题。 2.该问题在不同的行中有多个 UI 更新。因此,这种方式可能更容易。 3.此外,该方法在FormLoad中运行一次,我不担心资源使用情况。
【解决方案2】:

应避免不必要地在线程/上下文之间跳转。

这是一个更好的资源使用:

private async void form_Load(object sender, EventArgs e)
{
    pictureLoading.Visible = true;

    try
    {
        label1.Text = await LoadDgAsync();
    }
    catch
    {
        // error handling
    }
    finally
    {
        pictureLoading.Visible = false;
    }
}


private Task<string> LoadDgAsync()
{
    return Task.Run(() =>
    {
        string[] datas = db.row("select * from products");
        string[] datas2 = db.row("select * from users");
        double one = Convert.ToInt32(datas[0]);

        //....

        return one.toString();
    });
}

【讨论】:

    【解决方案3】:

    您正在同步调用loadDg() 函数。

    除非您等待loadDg() 函数调用(因为它的返回类型是Task)并使form_Load 函数异步,否则函数调用将是同步的。

    修复它的正确方法是......

    private async void form_Load(object sender, EventArgs e)
    {
      await loadDg();
    }
    

    【讨论】:

    • 这是错误的。调用异步方法不会同步运行它。如果该方法从一开始就是同步方法,则该方法只会同步运行,如果这是真的,那么这段代码的行为不会有任何不同,并且也会同步运行。 await 是一个在异步方法完成后更方便地做某事的工具。由于异步方法完成时这段代码没有任何作用,所以它什么也没做。
    • @Servy 也许您可以推荐另一种解决此问题的最佳方法?
    • @kevin73 你需要提供足够的代码来重现问题,以便任何人告诉你如何解决它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多