簡體   English   中英

使用await運行耗時的任務時UI被阻止

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

我想構建一個文件夾清理程序。 期望將刪除的文件實時報告給TextBox控件。 所以我在按鈕單擊事件中使用了一個await Task.Run(() => CleanFolder(folderPath, progress))函數。 但是用戶界面在運行時被阻止。 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。

但是我認為,真正的問題是將字符串和輸出連接到TextBox是非常低效的操作。

在您的情況下,最好在一行中或使用進度條顯示刪除進度。

這是我為您解決的問題的解決方案(我更改了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();
                });
            }
        }
    }

我希望這將有所幫助。

謝謝大家 簡而言之 ,這本書要歸功於C#6.0

我已經找到解決方案,並對異步/等待有了更好的了解。

首先,由於.Net Framework 4.5不建議使用Dispatcher.Invoke ,因此基於任務的異步已成為主要模式(使用async / awit)。

其次,有一些使用異步/等待的原則:

  • await之后的表達式必須是TaskTask<TResult>對象

  • 如果對方法使用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對象。

  • 如果使用async Task Foo()類的方法,則必須在其中使用await關鍵字。

  • 如果沒有什么可等待的,則刪除async修改器,使用return Task.Run(() => { Do Something });返回一個Task對象return Task.Run(() => { Do Something }); 現在,您可以在調用Foo()的方法中使用await Foo() Foo()

  • Task Foo()無法操作UI,但async Task Foo()可以。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM