等待和异步阻塞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?我错过了什么?

等待和异步阻塞UI

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)