【问题标题】:await and async blocking the UI等待和异步阻塞 UI
【发布时间】:2016-03-13 02:35:43
【问题描述】:

我写了一个小的 winforms 应用程序来搜索磁盘上的文件(为了这个问题,什么文件并不重要)。问题是它甚至可以是 100,000 个文件左右。所以这个操作需要时间。

我想要实现的是将搜索操作作为异步操作进行,而不是阻塞 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? 我错过了什么?

【问题讨论】:

  • mainDir 在哪里,里面有多少文件?
  • mainDir在c盘,目录树可以有200-300k的文件

标签: c# .net winforms asynchronous async-await


【解决方案1】:

async 关键字标记SearchPnrFilesAsync 本身并不会神奇地在单独的任务中异步启动此方法的执行。

事实上,SearchPnrFilesAsync 中除sw.WriteAsync 之外的所有代码都在 UI 线程中执行,从而阻塞了它。

如果你需要在单独的任务中执行你的整个方法,你可以通过如下包装来完成:

public async static Task SearchPnrFilesAsync(string mainDir)
{
   await Task.Run(() => your_code_here);
}

【讨论】:

【解决方案2】:

为什么 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);
        }   
    }   
}

【讨论】:

  • @dotctor 你不必这样做,这是一种方法。如果文件很大,您不想将其完全消耗到内存中。取决于 OP 需要什么。
  • @Yuval Itzchakov - 非常感谢这个想法,但这段代码仍然阻塞了 UI 线程。
  • 你可以使用Stream.CopyToAsync方法。
  • @Paulo 是的,也许这样更好。
  • @Paulo 是的,但是我讨厌给出使用正确异步方法的示例,然后在缺少异步对应物的情况下推迟到 Task.Run。但仍然可以使用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-04
  • 2011-04-03
  • 2019-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-12
相关资源
最近更新 更多