【问题标题】:async/await does not work properlyasync/await 不能正常工作
【发布时间】:2018-05-06 17:08:00
【问题描述】:

我的 async/await 方法有问题。我是 async/await 的新手,似乎无法正常工作。

当我有以下情况时,GUI 冻结。

private async void SetUpTextBox(string s)
{
    //This method is called in button event
    string textToSet = await GetTextA(s);
    read_Box.Text = textToSet;
}

private Task<string> GetTextA(string s)
{
    return Task.Run(() => GetText(s));
}

private string GetText(string s)
{
    string textToReturn = "Hello";
    using (StreamReader sr = new StreamReader(File.OpenRead(s)))
    {
        textToReturn = sr.ReadToEnd();
    }
    return textToReturn;
}

我不知道我做错了什么。我知道我是新手,这就是我来这里学习的原因(也就是不要评判!)。

P.S 当我试图改变时

using (StreamReader sr = new StreamReader(File.OpenRead(s)))
{
    textToReturn = sr.ReadToEnd();
}

使用简单的Thread.Sleep(2500) 方法。 GUI 根本不会冻结!

【问题讨论】:

  • 请注意,您的GetText 函数已经存在,它被称为File.ReadAllText
  • 在我没有提到之前我也试过了。它仍然冻结了 GUI。
  • 您提到了一个 GUI,但没有告诉我们是哪一个,也没有包括您如何连接到它。写一个正确的minimal reproducible example,包括ButtonClick 等。
  • 我有一个关于您为什么阻止 UI 的假设:您尝试放入行中的文本数量 read_Box.Text = textToSet;(在 UI 线程中运行)太多了。编辑:当然,当文本很小时,例如“Hello”,它不会阻塞 UI。
  • @HenkHolterman 对此感到抱歉。只是普通的winforms。

标签: c# asynchronous async-await


【解决方案1】:

避免使用async void(异步事件处理程序除外),此外,您可以使用ReadToEndAsync 并使代码始终异步。

private async Task SetUpTextBox(string s) {
    //This method is called in button event
    string textToSet = await GetTextAsync(s);
    read_Box.Text = textToSet;
}

private async Task<string> GetTextAsync(string s) {
    string textToReturn = "Hello";
    using (StreamReader sr = new StreamReader(File.OpenRead(s))) {
        textToReturn = await sr.ReadToEndAsync();
    }
    return textToReturn;
}

在按钮单击事件处理程序中调用 SetUpTextBox 的示例,正​​如原始帖子的评论所暗示的那样

public async void OnBtnClicked(object sender, EventArgs args) {
    try {
        //...

        await SetUpTextBox("some path");

        //..
    } catch {
        //... handle error
    }
}

您还应该使用 async void 事件处理程序在所有内容周围放置 try/catch,否则任何异常都不会被观察到。

参考Async/Await - Best Practices in Asynchronous Programming

【讨论】:

  • 我已经尝试过了,但 GUI 仍然冻结。此外,我最初想尝试“任务化”我创建的同步方法。如前所述,当我使用 Thread.Sleep(n); 更改它时,GUI 不会冻结;方法(这就是为什么有 string textToReturn = "Hello"; 只是为了在更改后返回一些东西来测试它)。但是,如果我使用流阅读器,它确实会冻结。而且我需要使用流阅读器。我也从 async void 更改为异步任务。
  • 可能会抛出异常,尝试将流式阅读器代码包装在 try-catch 中,确保捕获 AggregateException 或仅捕获所有内容,然后打印到控制台。
  • 我已经尝试过了,但我没有得到任何东西。 PS我已将其更改为控制台项目,因此它会在表单旁边打开控制台。
【解决方案2】:

首先,你应该做的事情,你不是:

  • 使用ReadToEndAsync
  • 当任务不受 CPU 限制时避免 Task.Run
  • 当方法不是事件处理程序时避免async void
  • 有适当的错误处理。

Nkosi 做得很好,说明了这一点。

剩下的就是解释为什么Thread.Sleep(2500) 不会阻止用户界面。


首先,请注意Task.Run 将使用ThreadPool 运行任务。

您可以在GetText 内部验证System.Threading.Thread.CurrentThread.IsThreadPoolThread 是否为真(例如,您可以设置断点并使用检查来检查)。

这意味着 UI 线程不能在 GetText 内部被阻塞,因为 UI 线程不会运行 GetText


但是为什么Thread.Sleep(2500); 会产生不同的结果呢?

嗯……有什么区别:

private string GetText(string s)
{
    string textToReturn = "Hello";
    using (StreamReader sr = new StreamReader(File.OpenRead(s)))
    {
        textToReturn = sr.ReadToEnd();
    }
    return textToReturn;
}

还有:

private string GetText(string s)
{
    string textToReturn = "Hello";
    Thread.Sleep(2500);
    return textToReturn;
}

让我告诉你:

  • 可能引发的异常。由于您没有处理它们 - 我假设您正在调试器中使用调试版本 - 您会注意到。所以,我不认为这是问题所在。即使你不是,异常也不是来自 UI 线程,会未经处理,并使应用程序崩溃(编辑:我测试过)。
  • 执行所需的时间量。但是,由于 UI 不会被此代码阻止 - 因为它在 ThreadPool 中运行 - 也不是这样。
  • 返回的文本量。

当你消除了不可能的事情后,剩下的,无论多么不可能,都必须是真相

-- 福尔摩斯

UI 卡住了,因为您返回的文本过多。


让我这样说……

返回的文本过多

private string GetText(string s)
{
    string textToReturn = "Hello";
    using (StreamReader sr = new StreamReader(File.OpenRead(s)))
    {
        textToReturn = sr.ReadToEnd();
    }
    return textToReturn;
}

返回了一小段文字:

private string GetText(string s)
{
    string textToReturn = "Hello";
    Thread.Sleep(2500);
    return textToReturn;
}

因此,UI 卡在read_Box.Text = textToSet;,忙于渲染您从文件中获得的所有文本。 或者至少这是我的假设。

【讨论】:

  • 是的“因此,UI 卡在 read_Box.Text = textToSet; 处,忙于渲染您从文件中获得的所有文本。或者至少这是我的假设。”。这就是问题所在。我拆分文本,然后以异步方式缓慢添加每一行以确认这一点。还要感谢你们向我展示的良好做法!.. 真的帮助了我,我学到了一些新东西!谢谢!
【解决方案3】:

在内部创建一个带有 Thread.Sleep(x) 的异步 void(例如 Wait),并用“await Wait(x) ;”调用它在 async void 内。

【讨论】:

  • 不要在异步代码中使用Thread.Sleep,使用Task.Delay(..)(你可以await
  • 建议创建一个异步 void 方法和一个 blocking 调用 Thread.Sleep() 当有类似 @ 987654324@
猜你喜欢
  • 2019-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-30
  • 2019-07-15
  • 2021-07-22
  • 2019-11-02
相关资源
最近更新 更多