【问题标题】:UI freezes when using async await使用异步等待时 UI 冻结
【发布时间】:2014-08-08 19:43:26
【问题描述】:

我无法使用异步方法使我的 UI 正常工作。这是我的代码的一部分

private async void btnDoOutput_Click(object sender, RoutedEventArgs e)
{
    /* Initiliaze */
    groupBoxConfiguration.IsEnabled = false;

    var progressIndicator = new Progress<int>();
    progressIndicator.ProgressChanged += (s,value) =>
    {
        progressExport.Value = (double)value;
        labelPercentage.Content = "Export in progress : " + value + " %";
    };
    /* do Work */

    switch (something)
    {
        case 1:
            await method(input, output, options, progressIndicator);
            break;
         default: break;
    }

    /* Finalization */  
    groupBoxConfiguration.IsEnabled = true;
}

方法是

public async static Task<string> method(string input, string output, string options, IProgress<int> progress)
{
    while(something)
    {
        //operations on input and output

        if (progress != null)
        {
            progress.Report(percentage);
        }
    }
}

当我点击我的按钮时,UI 冻结,groupBox 仍处于启用状态,直到最后才会显示进度。

【问题讨论】:

  • 这不可能是您的所有代码,因为您的方法签名无效。 method 不返回任何内容,但它是 Task&lt;string&gt;。 Async / await 不是“神奇地使我的代码异步”。它是“让这个已经异步的代码更容易使用。”我没有在您的代码中看到任何实际异步发生的事情。
  • 您可能会从这个 MSDN 链接中获得很多关于在异步方法中启用进度报告和取消的信息:blogs.msdn.com/b/dotnet/archive/2012/06/06/…
  • 我认为你需要类似return Task.Factory.StartNew&lt;string&gt;(()=&gt;{while(something){...}})

标签: c# wpf user-interface asynchronous async-await


【解决方案1】:

我认为您完全误解了 async / await 的实际工作原理。您的所有代码仍在 UI 线程上运行,因为您实际上并没有告诉它。这意味着您在method 上的await 毫无意义,因为无论如何它都会同步运行。

async/await 的目的是让调用代码有机会继续处理,直到遇到需要等待任务结果的代码部分。因此,在您的示例中,您需要更改 method 正文以实际返回可等待的 Task

public Task method(string input, string output, string options, IProgress<int> progress)
{
    return Task.Run(() => {
        while(something)
        {
            //operations on input and output

           if (progress != null)
           {
               progress.Report(percentage);
           }
        }
    });
}

【讨论】:

  • 没有理由在报告进度之前致电Invoke。使用Progress 类的全部要点 是它代表您管理编组到UI 线程的任务。您还应该使用Task.Run 而不是StartNew
  • @Servy 很公平,我不知道Progress 会自动执行此操作。无论如何我删除了这个例子,它只是不必要地膨胀了答案。
  • 另外,MyMethod 中的所有 cmets 都是错误的。 Calculating... notCalcSomething 运行时发生。 await 确保它在 CalcSomething 完成之前不会运行。
  • 另外,CalcSomething 不应该是 async。事实上,它不会编译,因为你已经在返回一个任务。你也没有在等待任何事情。
  • @Servy 这个例子在语法和逻辑上都是错误的(我没有在 IDE 面前)。
【解决方案2】:

首先请不要使用静态方法,尤其是静态工作者。

我认为问题在于您仍在您的 UI 线程上(我正在根据您提供的代码做出一些疯狂的假设)。尝试使用Task&lt;string&gt;.Factory.StartNew(...),它应该会自动从 UI 线程中调用。

注意可能需要使用调度程序并调用回 UI 线程以使您的进度条正常工作。

【讨论】:

  • UI 线程是同步的(这就是他在执行工作时冻结的原因),当您启动一个新任务时,TPL 将自动调用到后台线程,这将使您的 UI 保持响应。
猜你喜欢
  • 1970-01-01
  • 2017-08-22
  • 1970-01-01
  • 1970-01-01
  • 2013-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-13
相关资源
最近更新 更多