简体   繁体   English

等待和异步阻塞 UI

[英]await and async blocking the UI

I wrote a little winforms application that search for files on the disk (what file is not that important for the sake of the question).我写了一个小的 winforms 应用程序来搜索磁盘上的文件(为了这个问题,什么文件并不那么重要)。 the problem is the that it can be even 100,000 files or so.问题是它甚至可以是 100,000 个文件左右。 so this operation takes time.所以这个操作需要时间。

What I want to achieve is to do the search operation as an async operation and not to block the UI thread so the form won't get stuck.我想要实现的是将搜索操作作为异步操作进行,而不是阻塞 UI 线程,这样表单就不会卡住。

I can do this with the backgroundWorker but for some reason not with the async\await mechanism.我可以用 backgroundWorker 做到这一点,但由于某种原因不能用 async\await 机制。

Here is my code:这是我的代码:

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);
        }
    }
}

Why is the UI thread get stuck and not displaying the MessageBox immediately?为什么 UI 线程卡住而不立即显示MessageBox what am I missing?我错过了什么?

Why does the UI thread get stuck? 为什么UI线程卡住了?

Probably because you have some blocking work done on the UI thread, such as reading a file. 可能是因为您在UI线程上完成了一些阻塞工作,例如读取文件。

Why is my MessageBox not displayed immediately? 为什么我的MessageBox不会立即显示?

Because that is not how async works. 因为这不是async工作原理。 When you await a method, it asynchronously yields control to the caller until the operation is done. 当您await一个方法时,它会异步地向调用者发出控制权,直到操作完成。 When the first await is hit, you start reading files from disk and copying them. 当第一个await命中时,您开始从磁盘读取文件并复制它们。 await does not mean "execute this on a different thread and continue". await并不意味着“在不同的线程上执行此操作并继续”。

What you probably want to do is use an asynchronous reading mechanism instead of the blocking File.ReadAllText . 您可能想要做的是使用异步读取机制而不是阻塞File.ReadAllText You can do this using StreamReader : 您可以使用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);
        }   
    }   
}

The fact of marking SearchPnrFilesAsync with async keyword itself doesn't magically starts execution ot this method asynchronously in separate task. async关键字本身标记SearchPnrFilesAsync的事实并没有神奇地在单独的任务中异步地开始执行此方法。

In fact, all of the code in SearchPnrFilesAsync except sw.WriteAsync executes in UI thread thus blocking it. 事实上,所有的码的SearchPnrFilesAsync除了sw.WriteAsync执行UI线程从而阻止它。

If you need to execute your whole method in separate task, you can do it by wrapping like: 如果你需要在单独的任务中执行整个方法,你可以通过包装如下:

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

Why is the UI thread get stuck为什么UI线程卡住了

I've tested the code you posted doesn't make UI blocked.我已经测试过您发布的代码不会阻止 UI。 When SearchPnrFilesAsync running, I was able to resize the window and click the button as well.SearchPnrFilesAsync运行时,我能够调整 window 的大小并单击按钮。 Did you think it was blocked because it didn't show the "After SearchPnrFilesAsync" message?您是否认为它被阻止是因为它没有显示“在 SearchPnrFilesAsync 之后”消息? If Something I wrong, please let me know.如果我错了,请告诉我。

not displaying the MessageBox immediately不立即显示 MessageBox

The await keyword inside the button_FindFiles_Click function waits for the SearchFilesUtil.SearchPnrFilesAsync function to finish. button_FindFiles_Click function 中的await关键字等待SearchFilesUtil.SearchPnrFilesAsync function 完成。 That's why "After SearchPnrFilesAsync" message doesn't pop up as soon as the button is clicked.这就是为什么单击按钮后不会立即弹出“在 SearchPnrFilesAsync 之后”消息的原因。 If you want the SearchFilesUtil.SearchPnrFilesAsync function to be executed as soon as you click the button and immediately you want to check the message, you can call the method without await keyword.如果您希望在单击按钮后立即执行SearchFilesUtil.SearchPnrFilesAsync function 并立即检查消息,则可以在不使用await关键字的情况下调用该方法。

SearchFilesUtil.SearchPnrFilesAsync(this.textBox_mainDirectory.Text);

When you don't use await on a function that returns a Task , you'll get a "warning".当您不在返回Task的 function 上使用await时,您将收到“警告”。 In this case, use discards feature and the warning will disappear.在这种情况下,使用discards功能,警告将消失。 (With C# 7) (配C# 7)

_ = SearchFilesUtil.SearchPnrFilesAsync(this.textBox_mainDirectory.Text);

If you want to know more about it, please check out this link ( https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/discards )如果您想了解更多信息,请查看此链接( https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/discards

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM