使用异步方法时,是否必须使用 "await" 关键字?

3
我有以下方法:
async Task A()
{
    HttpClient client = new HttpClient();
    string response = await client.GetStringAsync("http://msdn.microsoft.com");        
}

async Task B()
{
    HttpClient client = new HttpClient();
    string response = await client.GetStringAsync("http://google.com");
}

我将成为一个通用方法的调用者,该方法涉及远程API的调用、响应处理,并且不返回任何内容。
我将像这样调用它:
protected void Page_Load(object sender, EventArgs e)
{
    A();
    B();

    // Do further task after this which is not dependent on response of A or B
}

编译器对 A 和 B 方法发出以下警告:

由于此调用未被等待,因此在完成调用之前,当前方法的执行将继续进行。请考虑将 'await' 操作符应用于调用的结果。

enter image description here

我不需要从A或B方法中得到任何响应,所以我考虑在A()和B()前省略"await"关键字,因为我想在A和B调用后执行代码,我已经写了注释"//在此之后执行进一步的任务,这不依赖于A或B的响应",而不必等待A或B的响应。
我可以忽略编译器警告吗?
我是否误用了API?
我不能删除A和B方法上的异步运算符,因为HttpClient.GetStringAsync要求使用它。
编辑:
我去了这个链接https://softwareengineering.stackexchange.com/questions/331353/does-omitting-await-keyword-once-in-the-call-stack-break-the-asynchronous-behavi 并找到以下评论:
"当你不等待异步方法时,它相当于调用Task.Run(()=>A())。"
这是真的吗?

await Task.Run(()=>{A();B();}); - Lei Yang
你对 response 对象做了什么?如果在调用 AB 后的代码不需要执行 AB,那么你的方法就可以。 - Zein Makki
3
已经有很多关于“c# fire-and-forget”的问题了……请确保[编辑]帖子以展示您的研究结果——否则人们可能会认为您甚至没有尝试过,并会对问题进行投票降权。 翻译:已经有很多关于“c# fire-and-forget”的问题了,请务必编辑帖子以展示您的研究成果,否则人们可能会认为您甚至没有尝试过,从而对问题进行投票降低其评分。 - Alexei Levenkov
@Raghav,你的问题是如何确保在ASP.NET中未等待的任务运行完成?是这样吗? - Zein Makki
关于您的编辑 - 请阅读底部评论,其中已经指出它与 Task.Run(()=>A()); 不等价。 - Damien_The_Unbeliever
显示剩余5条评论
4个回答

0

基本上,await 确保 Task 完成其执行。在没有使用 await 的情况下运行 async 方法就像仅启动 Task 而不监视其进度和结果。

例如,在 ASP.Net 环境中,如果从请求线程启动任务,则可能导致任务被中止等问题。


运行异步方法而不等待就像启动任务而不监视其进度和结果一样。我对此没有任何问题,即使不监视其进度和结果,但如果在调用时不应用await关键字,是否保证代码会执行或会在中途中断/丢失呢? - Raghav
1
@Raghav - 如果你不关心他们的进展或结果,那么你可以将方法体替换为空的,或者完全删除方法调用。 - Damien_The_Unbeliever
@Damien_The_Unbeliever 我确实关心我在 A 和 B 中编写的代码的完成情况,尽管我不关心这些方法返回什么。由于我是从 ASP.NET 页面加载中调用它,所以我必须在 A 和 B 前面加上 await 来保证其完成,省略 await 可能会导致这些方法被中止(没有 await 关键字),对吗? - Raghav

0

更新:ASP.NET中的Fire and Forget

在你的代码中,在A或B的响应不依赖的情况下执行进一步任务部分会在方法AB完成之前执行(除非这些方法非常快)。

当你使用await并且该方法命中该行时,它会向调用者返回一个Task,而调用者继续执行第二行(在你的情况下是B),由于B也是如此,因此在调用这两个方法后执行其后部分。

附注:

每个使用await关键字的方法都应标记为异步,这是为了向后兼容。然而,编译器只是警告你在调用者中没有等待结果,对于初学者来说,这是一个很好的警告。


0
如果您想要的是“发射并忘记”,则在大多数情况下可以安全地忽略警告(除了 ASP.NET 等特定情况)。一些库,例如 Microsoft.VisualStudio.Threading,甚至提供了帮助程序来抑制这些警告:https://msdn.microsoft.com/en-us/library/microsoft.visualstudio.threading.tplextensions.forget.aspx 您可以按照以下方式使用它:
// Forget doesn't actually do anything internally. 
// It's an empty extension method you use to prove that you actually want the task to be
// fire and forget
A().Forget();
B().Forget();

如果您在同步上下文中执行方法,并且该方法在内部执行了await,但没有使用ConfigureAwait(false),那么可能会出现一些奇怪的行为。这就是为什么库代码中应始终使用.ConfigureAwait(false)的原因之一。


针对您的第二个问题:
“当您不等待异步方法时,它是否相当于调用Task.Run(()=>A())?”
这是错误的。至少有两个主要区别:
  • 使用 async 方法时,第一个 await 之前的部分将在调用线程中同步执行。例如:

    public async Task DoSomething()
    {
        Thread.Sleep(1000); // 将在调用线程中执行
        await SomeExternalResource();
        Thread.Sleep(1000); // 将在 continuation 中执行
    }
    

    在这里,如果直接调用方法,则调用线程将被阻塞至少 1 秒钟(至少,因为有可能 continuation 会在同一线程中执行)。如果使用 Task.Run,整个方法将在线程池上执行,不会阻塞调用线程。

  • 使用 Task.Run 执行方法时,不会继承当前的同步上下文,这在某些情况下可能会显著改变其行为。


  • @Itsik,我不明白.ConfigureAwait(false)怎么会导致死锁。你能详细解释一下吗? - Kevin Gosse
    如果在同步上下文中执行方法并且该方法内部执行了一个没有使用ConfigureAwait(false)的await,那么您也可能会遇到一些奇怪的行为。是的,其中之一就是死锁,当从UI同步上下文调用异步方法时可能会出现死锁。 - Raghav
    @KevinGosse 简而言之,我可以在调用异步方法时不使用await关键字,而该异步方法将会完整执行,对吗? - Raghav
    1
    @KevinGosse 我正在阅读这篇文章http://stackoverflow.com/questions/29577652/calling-an-async-method-without-awaiting,并发现Stephan的回复:“在ASP.NET上,如果您启动了这样的独立工作(即与HTTP请求无关的工作),那么该工作可能会在您不知情的情况下被中止。” 这个评论再次让我陷入困境,无法确定A或B方法是否会完成。 - Raghav
    @KevinGosse 这意味着我必须应用 await 来保证它们的完成,因为我是从我的 ASP.NET 页面的 Pageload 事件中调用它们。因此,省略 await 可能会导致这些方法被中止(没有 await 关键字),对吧?也许这将是我在这个线程上的最后一个查询 :) - Raghav
    显示剩余8条评论

    0
    如果在 Page_Load 的其余流程中,A 和 B 的执行是异步的,那么您就不必等待它们。await
    除了这里提供的选项之外,另一个选择是将其用 #pragma disable 包装起来,以向阅读您代码的其他人明确表明您正在忽略此警告。
    #pragma warning disable warning-list  
    
    A();
    B();
    
    #pragma warning restore warning-list 
    

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接