【问题标题】:ReadToEndAsync is blocking, how to get it to run asynchronously?ReadToEndAsync 被阻塞,如何让它异步运行?
【发布时间】:2015-05-04 11:21:03
【问题描述】:

我从控制器中的异步操作调用此 getJson() 函数。 我有一个大文件列表,每个文件都需要几秒钟才能处理 ReadToEndAsync()。 即使我正在等待,ReadToEndAsync() 也会阻塞。

如何异步调用 ReadToEndAsync()?

public static async Task<T> getJson<T>(String url)
{
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(server + url);
    HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync();

    String jsonString;
    //get the json string from the response
    using (var xx = response.GetResponseStream())
    {
        StreamReader reader = new StreamReader(xx, Encoding.UTF8);
        jsonString = await reader.ReadToEndAsync()
    }
}

控制器:

_model.list.Add(await DataRetriever.getJson<ReviewModel>(url));
_model.list.Add(await DataRetriever.getJson<ReviewModel>(url));
_model.list.Add(await DataRetriever.getJson<ReviewModel>(url));

【问题讨论】:

  • ReadToEndAsync() 正在阻塞,因为您正在等待!
  • 如果我不等待它,它仍然会阻塞。我怎样才能让它异步运行?
  • 在调试器中,在返回之前不会执行其他代码。
  • 使用线程窗口找到正确的线程。或发布任何其他证明 ReadToEndAsync 实际上阻塞了该线程的证据。我不明白你是如何确定 ReadToEndAsync 确实是阻塞函数。
  • @RjGeraci VS 调试器是异步感知的。如果您跨过,它将在等待调用完成时暂停。所以可能 ReadToEndAsync 是异步的,但调试器对你隐藏了它。那有意义吗?你还认为通话被阻塞了吗?

标签: c# .net asp.net-mvc stream


【解决方案1】:

如 cmets 中所述,虽然方法 ReadToEndAsync() 本身确实阻塞 - 在通常意义上的“阻塞”一词中:也就是说,调用该方法的线程继续运行——它肯定会阻止await之后的代码执行,直到方法完成。这正是您想要的:您不希望在读取完成之前执行该代码,否则它将没有结果可操作。

您可能应该(重新?)阅读有关asyncawait 的文档,但要简单地说:关键字async 应用于一个方法,以向编译器发出信号,表明它将使用awaitawait 用于向编译器指示方法中的一个点,编译器应保存方法的当前执行状态并返回

如果并且当应用了await 关键字的操作完成时,框架将自动继续执行它停止的方法。在与同步上下文相关的线程(例如 Winforms 或 WPF 程序中的 GUI 线程)中调用该方法时,默认情况下,此“继续”在该原始线程中执行(但可以在适当时禁用此功能)。

请注意上面的async 方法返回来自await 语句。因此,方法本身不会阻塞线程,即使方法所代表的操作在某种意义上确实“阻塞”了。


因此,综上所述,在我看来,您可能正试图让您的三个操作全部异步和并发完成。如果你想这样做,你需要立即启动所有异步操作,而不是像现在这样等待每个操作完成后再继续下一个操作(即通过 await 语句应用于对 @ 的调用987654333@方法)。

看起来像这样:

Task<ReviewModel> task1 = DataRetriever.getJson<ReviewModel>(url1),
    task2 = DataRetriever.getJson<ReviewModel>(url2),
    task3 = DataRetriever.getJson<ReviewModel>(url3);

_model.list.Add(await task1);
_model.list.Add(await task2);
_model.list.Add(await task3);

(注意:在您的原始代码示例中,您只使用了一个 url 值。因为我假设您真的不想只执行三个不同的查询,所以我继续更改了代码示例,所以每个任务使用不同的值)。

在上面,即使第二个和/或第三个任务在第一个之前完成,结果也会按顺序添加到结果列表中。 但是任务本身都是同时启动的,所以它们可以同时运行。


我希望以上内容不仅可以为您澄清对方法 ReadToEndAsync() 的调用是否阻塞(它没有,尽管由于调试器呈现代码执行的方式,它可能 似乎 em> 确实如此),但也为您提供了解决导致您调试该方法并认为它正在阻塞的更大问题的解决方案。 IE。您的 JSON 查询没有同时执行。

如果在您的问题的 cmets 和上述答案之间,您仍然有问题,请编辑您的问题,以便更清楚地了解您到底在问什么。

【讨论】:

  • 很好的答案,但您在同一线程中恢复的描述 await 不正确。只有基于 UI 的上下文与单个线程相关联; ASP.NET 的工作方式非常不同。 ASP.NET 上下文是“请求上下文”,因此只要将“请求上下文”移至恢复线程,ASP.NET 上的await 就可以在另一个线程上恢复是完全有效的(并且很常见) .
  • @StephenCleary:感谢您的澄清。我同意将 ASP.NET 行为如此广泛地与 GUI API 的线程拥有上下文模型混为一谈是错误的。我会改写这段话。
  • 谢谢,这确实让事情变得更清楚了。我实际上是在执行相同的查询 3 次,服务器返回一个随机对象。
  • 因为您要单独等待每个任务:也许Task.WhenAll 更好? msdn.microsoft.com/en-us/library/…
  • @JP: 使用WhenAll() 将是不必要的复杂化,尤其是因为无论如何都需要解压缩所有值并将其添加到列表中。此外,您的编辑将有效代码变成了无效代码,坦率地说,即使它是有效的,也没有以任何方式改进代码。这种变化完全是主观的。请查看有关何时以及如何编辑帖子的 Meta 讨论,并注意严格遵循该指南。
猜你喜欢
  • 2015-05-05
  • 1970-01-01
  • 1970-01-01
  • 2014-07-29
  • 2018-04-06
  • 1970-01-01
  • 2022-12-21
  • 2017-05-08
  • 1970-01-01
相关资源
最近更新 更多