简体   繁体   English

使用await运行耗时的任务时UI被阻止

[英]UI blocked when using await to run a time consuming task

I want to build a folder cleaner program. 我想构建一个文件夹清理程序。 It is expected to report deleted files to a TextBox control at real-time. 期望将删除的文件实时报告给TextBox控件。 So I use await Task.Run(() => CleanFolder(folderPath, progress)) function in my button click event. 所以我在按钮单击事件中使用了一个await Task.Run(() => CleanFolder(folderPath, progress))函数。 But the UI blocked when running. 但是用户界面在运行时被阻止。 After a while when the CheanFolder() method run complete, all the deleted files are showed at one time. CheanFolder()方法运行完成一段时间后,所有删除的文件会同时显示。

namespace FolderCleaner
{
    public partial class MainWindow : Window
    {
        string folderPath;
        string matchPattern;

        private void ButtonOpen_Click(object sender, RoutedEventArgs e)
        {
            FolderBrowserDialog fbd = new FolderBrowserDialog() { Description = "Select a folder" };
            if (fbd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                folderPath = fbd.SelectedPath;
                textBoxPath.Text = folderPath;
                buttonClean.IsEnabled = true;
                textBoxList.Text = "Folder path: " + folderPath + "\n";
            }
        }

        private async void ButtonClean_Click(object sender, RoutedEventArgs e)
        {
            matchPattern = textBoxPattern.Text;
            buttonOpen.IsEnabled = false;
            buttonClean.IsEnabled = false;
            Progress<string> progress = new Progress<string>(msg =>
            {
                textBoxList.AppendText("File deleted: " + msg + "\n");
                textBoxList.CaretIndex = textBoxList.Text.Length;
                textBoxList.ScrollToEnd();
            });

            try
            {
                await Task.Run(() => CleanFolder(folderPath, progress));

                textBoxList.AppendText("Mission complete!");
                textBoxList.CaretIndex = textBoxList.Text.Length;
                textBoxList.ScrollToEnd();
            }
            catch
            {
                System.Windows.MessageBox.Show("Error!");
            }
            finally
            {
                buttonOpen.IsEnabled = true;
            }
        }

        private void CleanFolder(string path, IProgress<string> progress)
        {
            var filePaths = Directory.EnumerateFiles(path, "*.*", System.IO.SearchOption.AllDirectories);
            foreach (var filePath in filePaths)
            {
                var matchResult = Regex.Match(filePath, matchPattern);
                if (matchResult.Success)
                {
                    File.Delete(filePath);
                    progress.Report(filePath);
                }
            }
        }
    }
}

GUI can`t be controlled from another thread. 不能从另一个线程控制GUI。

But i think, that real problem is that concatenating of string and output to a TextBox is a very inefficient operation. 但是我认为,真正的问题是将字符串和输出连接到TextBox是非常低效的操作。

In your case it is better to show progress of removal in a single line or by using the progress bar. 在您的情况下,最好在一行中或使用进度条显示删除进度。

Here is my solution of your problem (i`ve changed 2 methods): 这是我为您解决的问题的解决方案(我更改了2种方法):

    private async void ButtonClean_Click(object sender, RoutedEventArgs e)
    {
        matchPattern = textBoxPattern.Text;
        buttonOpen.IsEnabled = false;
        buttonClean.IsEnabled = false;

        await Task.Run(() => CleanFolder(folderPath));

        textBoxList.Text += "Mission complete!";
        buttonOpen.IsEnabled = true;
    }

    private void CleanFolder(string path)
    {
        var filePaths = Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories);
        foreach (var filePath in filePaths)
        {
            var matchResult = Regex.Match(filePath, matchPattern);
            if (matchResult.Success)
            {
                File.Delete(filePath);
                System.Windows.Application.Current.Dispatcher.Invoke(delegate
                {
                    // this working fast
                    textBoxList.Text  = "File deleted: " + filePath + "\n";
                    // this working slow and slower over time
                  //textBoxList.Text += "File deleted: " + filePath + "\n";
                    textBoxList.ScrollToEnd();
                });
            }
        }
    }

I hope this will help. 我希望这将有所帮助。

Thanks for everyone. 谢谢大家 Thanks to the book C# 6.0 in a nutshell 简而言之 ,这本书要归功于C#6.0

I have figured out the solution and have a better understanding of async/await. 我已经找到解决方案,并对异步/等待有了更好的了解。

First of all, Dispatcher.Invoke is not recommended to use since .Net Framework 4.5, task-based asynchrony has become the dominant pattern (using async/awit). 首先,由于.Net Framework 4.5不建议使用Dispatcher.Invoke ,因此基于任务的异步已成为主要模式(使用async / awit)。

Second, there are a few principles of using async/await: 其次,有一些使用异步/等待的原则:

  • The expression after await must be a Task or Task<TResult> object await之后的表达式必须是TaskTask<TResult>对象

  • If you use async modifier to a method, then the method don t need to return a Task method manually. The compile will wrap the method as a 如果对方法使用async修饰符,则该方法t need to return a method manually. The compile will wrap the method as a t need to return a Task method manually. The compile will wrap the method as a method manually. The compile will wrap the method as a Task` object. method manually. The compile will wrap the method as a Task对象。

  • If you use a method like async Task Foo() , you must use an await keyword in it. 如果使用async Task Foo()类的方法,则必须在其中使用await关键字。

  • If there is nothing to await, then remove the async modifier, return a Task object by using return Task.Run(() => { Do Something }); 如果没有什么可等待的,则删除async修改器,使用return Task.Run(() => { Do Something });返回一个Task对象return Task.Run(() => { Do Something }); . Now you can use await Foo() in the method that calling Foo() . 现在,您可以在调用Foo()的方法中使用await Foo() Foo()

  • Task Foo() can not operate UI, but async Task Foo() can. Task Foo()无法操作UI,但async Task Foo()可以。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在ASP.NET Web应用程序中使用async / await时,UI线程被阻止 - UI thread is being blocked when using async/await with Task in ASP.NET web application C# 理解阻塞的 UI 和异步/等待与 Task.Run 的问题? - C# Understanding trouble with blocked UI and async / await vs. Task.Run? 使用await与Task.Run,​​但UI仍然挂起几秒钟? - Using await with Task.Run but UI still hangs for a few seconds? C#任务异步等待智能卡-UI线程被阻止 - C# task async await smart card - UI thread blocked 在基于 UI 的应用程序中使用 await Task.Run(() =&gt; someMethodAsync()) 与 await someMethodAsync() - Using await Task.Run(() => someMethodAsync()) vs await someMethodAsync() in a UI based app 用户界面被异步/等待阻止 - UI blocked with Async/Await 使用async / await和Task.Run时,不会显示特定的异常 - Specific exceptions are not shown when using async/await and Task.Run 等待Dispatcher.BeginInvoke…(使用WPF和WCF)时,我的UI线程被阻止了 - My UI thread is blocked when await Dispatcher.BeginInvoke… (using WPF and WCF) C#/ WPF使用异步并等待,但UI线程仍被阻止 - C#/WPF Using async and await but the UI thread is still blocked 具有耗时任务的列表框选择,需要更新UI - List box selection with time consuming task that needs to update the UI
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM