![](/img/trans.png)
[英]UI thread is being blocked when using async/await with Task in ASP.NET web application
[英]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
之后的表達式必須是Task
或Task<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.