等待和异步阻塞UI
本文关键字:UI 异步 等待 | 更新日期: 2023-09-27 18:26:01
我写了一个小的winforms应用程序,用于搜索磁盘上的文件(为了这个问题,什么文件不那么重要)。问题是,它甚至可以是100000个左右的文件。所以这个操作需要时间。
我想要实现的是将搜索操作作为异步操作进行,而不是阻塞UI线程,这样表单就不会陷入困境。
我可以使用backgroundWorker来完成这项工作,但由于某些原因,不能使用async''await机制。
这是我的代码:
private async void button_FindFiles_Click(object sender, EventArgs e)
{
await SearchFilesUtil.SearchPnrFilesAsync(this.textBox_mainDirectory.Text);
MessageBox.Show("After SearchPnrFilesAsync");
}
public async static Task SearchPnrFilesAsync(string mainDir)
{
foreach (string file in Directory.EnumerateFiles(mainDir, ".xml", SearchOption.AllDirectories))
{
var fileContenet = File.ReadAllText(file);
var path = Path.Combine(@"C:'CopyFileHere", Path.GetFileName(file));
using (StreamWriter sw = new StreamWriter(path))
{
await sw.WriteAsync(fileContenet);
}
}
}
为什么UI线程被卡住并且没有立即显示MessageBox
?我错过了什么?
用async
关键字标记SearchPnrFilesAsync
本身并不能神奇地在单独的任务中异步启动此方法的执行。
事实上,除了sw.WriteAsync
之外,SearchPnrFilesAsync
中的所有代码都在UI线程中执行,因此阻塞了它
如果你需要在单独的任务中执行你的整个方法,你可以通过包装来完成,比如:
public async static Task SearchPnrFilesAsync(string mainDir)
{
await Task.Run(() => your_code_here);
}
为什么UI线程卡住了?
可能是因为您在UI线程上完成了一些阻塞工作,例如读取文件。
为什么我的MessageBox没有立即显示?
因为async
不是这样工作的。当您await
一个方法时,它会异步地将控制权交给调用者,直到操作完成。当第一个await
被命中时,您开始从磁盘读取文件并复制它们。await
并不意味着"在不同的线程上执行此操作并继续"。
您可能想要做的是使用异步读取机制,而不是阻塞File.ReadAllText
。您可以使用StreamReader
:执行此操作
public static async Task SearchPnrFilesAsync(string mainDir)
{
foreach (string file in Directory.EnumerateFiles(mainDir, ".xml",
SearchOption.AllDirectories))
{
var path = Path.Combine(@"C:'CopyFileHere", Path.GetFileName(file));
using (var reader = File.OpenRead(file))
using (var writer = File.OpenWrite(path))
{
await reader.CopyToAsync(writer);
}
}
}
为什么UI线程被卡住
我已经测试过你发布的代码不会阻止UI。当SearchPnrFilesAsync
运行时,我可以调整窗口大小并单击按钮。你认为它被屏蔽是因为它没有显示";在SearchPnrFilesAsync"之后;消息如果我出了什么问题,请告诉我。
不立即显示MessageBox
button_FindFiles_Click
函数内的await
关键字异步等待SearchFilesUtil.SearchPnrFilesAsync
函数完成。这就是为什么"在SearchPnrFilesAsync"之后;单击按钮后不会立即弹出消息。如果希望单击按钮后立即执行SearchFilesUtil.SearchPnrFilesAsync
功能并且您想立即检查消息,就可以调用不带await
关键字的方法。
SearchFilesUtil.SearchPnrFilesAsync(this.textBox_mainDirectory.Text);
当你不在返回Task
的函数上使用await
时,你会得到一个";警告";。在这种情况下,使用discards
功能,警告将消失。(带C#7)
_ = SearchFilesUtil.SearchPnrFilesAsync(this.textBox_mainDirectory.Text);
如果你想了解更多信息,请查看此链接(https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/discards)