【问题标题】:Does it make sense to call only one `async` method and `await` for it in another `async` method?只调用一个 `async` 方法并在另一个 `async` 方法中为它调用 `await` 有意义吗?
【发布时间】:2020-07-07 09:06:45
【问题描述】:

在另一个async 方法中只调用一个async private 方法和await 是否有意义? 示例:

private async Task<string> ReadMyFileAsync()
{
    var streamReader = new StreamReader(@"c:\myFile.txt")

    return await streamReader.ReadToEndAsync(); // <-- Does it make sense for me here to await for only onething, while the caller of the method ReadMyFileAsync is already an async method?
    // By ignoring that the stream should be disposed...
}

private string ReadMyFile()
{
    var streamReader = new StreamReader(@"c:\myFile.txt")

    return streamReader.ReadToEnd();
    // By ignoring that the stream should be disposed...
}

public async void OnFileChangedHandler()
{
    // Some work...
    var myText = await ReadMyFileAsync(); <-- Does it make any difference whether I  called `await ReadMyFileAsync()` or `ReadMyFile()`?

    var someThingElse = await SomeThingElse(...);
    // Some work...
}

在上面的场景中,我猜,即使 ReadMyFileAsync 方法在其同步版本中被调用(不使用 async 并且使用 ReadToEnd() 而不是 await ReadToEndAsync()),它也会保持相同的行为, 因为调用者方法是一个async 方法。这意味着在我看来,这只有在我尝试并行读取多个文件时才有意义(参见以下示例),对吗?

private async Task<string> ReadMyFileAsync() // <-- This makes sense for me
{
    var streamReader1 = new StreamReader(@"c:\myFile1.txt");
    var streamReader2 = new StreamReader(@"c:\myFile2.txt");
    var streamReader3 = new StreamReader(@"c:\myFile3.txt");

    var myText1Task = streamReader1.ReadToEndAsync();
    var myText2Task = streamReader2.ReadToEndAsync();
    var myText3Task = streamReader3.ReadToEndAsync();

    return await myText1Task + await myText2Task + await myText3Task;

    // By ignoring that the streams should be disposed...
}

所以问题很简单,在第一段代码中,我调用ReadMyFileAsync 还是ReadMyFile 有什么区别吗?

更新:整个问题简化为:

如果我要在我的异步方法OnFileChangedHandler 中等待异步方法ReadMyFileAsync,我为什么要使用ReadMyFileAsync 而不是ReadMyFile

【问题讨论】:

  • 对不起,我不明白这个问题。您是在问单行方法是否有意义?
  • 这是一个有趣的问题,我的直觉告诉我,不,由于您所描述的原因,这样做是没有意义的。两个 await 最终都会等待同一个工作。
  • @ZoharPeled,感谢您的反馈,我在底部添加了一行,并在第一个代码块中添加了一个同步版本的方法。我希望现在更清楚了。
  • 关于这个主题有整本书。这是快速google search 和旧Jon Skeet one 的结果
  • @Liam 我认为 nvoigt 的回答是回答“我应该等待我的异步方法还是应该将任务传递下去”的问题。虽然问题更多的是“我为什么更喜欢 DoXXXAsync 而不是 DoXXX”

标签: c# .net asynchronous async-await


【解决方案1】:

这取决于。如果您真的只调用了另一种方法并且没有副作用,那么您可以只返回 Task 而不是 awaiting 它并返回包装在任务中的结果。

但是,即使在您的示例中,您也有副作用。你需要.Dispose 一些东西,你需要知道什么时候这样做。所以你不能只返回任务,你需要等到它完成,然后释放你的资源,然后返回完成任务的结果。由于即使在您自己的简化示例中它也“不是那么简单”,所以答案是:它确实有意义。您必须根据具体情况做出决定。

有时你可以直接传递任务,那么方法根本不需要async,它只需要返回一个Task,有时你需要根据任务的时间来做事情(即仅在其他事情完成后才执行此操作)然后您需要使用async 方法,因为您想要await 结果。

【讨论】:

  • 谢谢你的回答,实际上我的重点在你的第一行:调用私有方法(ReadMyFile 或 ReadMyFileAsync)的 sycn 或异步版本有什么不同我的代码在第一个块中? (我在第一个块中添加了一些代码)。我会说不,因为完成工作所花费的时间将保持不变。还是我错过了什么?当然忽略我的例子中的副作用。
  • 我添加了一个小更新,简化了整个问题,你能看一下吗?
  • 在我简单的答案后面隐藏着一篇 10 页的文章。在某些情况下,让某些东西返回可以等待的Task 具有优势。但是,如果您没有其中一种情况,请不要使用不必要的 async/await 调用使您的代码复杂化。换句话说:如果正常工作,不要竭尽全力使其异步。
  • @nvoigt 在编写大型应用程序时,从 get go 开始使用异步方法确实有意义,因为否则您会发现自己需要将整个方法链重构为异步。此外,现代 .net 在任何地方都支持异步,包括 cli 应用程序。所以在任何地方都使用 async 一点也不难。
【解决方案2】:

是的,您应该更喜欢 async 方法而不是同步方法,因为异步方法不会阻塞正在执行的线程。在 UI 应用程序中,这意味着 UI 不会冻结。在服务器应用程序中,这意味着您的服务器将能够运行更多并发工作而不会导致线程饥饿。

【讨论】:

  • @MohammedNoureldin 你的意思是调用同步方法与异步 inside 异步函数有什么区别?好吧,将async 添加到您的方法定义中不会使您的方法异步。如果您在此方法中只有同步调用,那么整个方法将是同步的。
  • @MohammedNoureldin 它的工作方式不同。同步方法将使正在执行的线程休眠,什么也不做。 await 将安排继续。线程在等待时可能会做其他工作。
  • 这是一个非常广泛的概括。 async/await 模式带来了很多它自己的困难,如果简单的同步调用适用于您的情况,那么给您的应用程序带来额外的复杂性是没有意义的。
  • 在现代 .net 中使用异步一点也不难。现在每个现代项目结构都支持甚至强制执行异步。
  • 您假设async 是免费的。它不是。异步在线程同步方面是有代价的。如果您在 CPU 繁忙的情况下进行 CPU 绑定处理,则此线程利用率成本只会使您的代码变慢。 async/await 是 IO 绑定操作的正确答案。但是你不能简单地声明use asyncactual answer 是,这取决于
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-04-06
  • 2021-05-02
  • 2017-05-13
  • 1970-01-01
  • 1970-01-01
  • 2019-10-15
  • 1970-01-01
相关资源
最近更新 更多