提问者:小点点

C#我可以使用discardable来避免等待异步函数调用吗?


我有一个同步函数getUserStatus(),它反过来调用一个异步函数logAsync()

我不想等待logAsync(),但这似乎不像声明await那样简单。 我只是希望这不是等待的问题,但不是。

我知道有一些方法,比如使用.continueWith(),但是在这个特定的例子中,我想知道如果仅仅放弃对logAsync()的调用,就会让当前线程继续,而不需要等待logAsync()完成?

首选伪代码设置:

Public UserStatus GetUserStatus(){
    var status = GenerateStatus();
    _logger.LogAsync("Current Status: " + status.description()); //invoke and forget
    return status;
}

现实伪码:

Public async UserStatus GetUserStatus(){
    var status = GenerateStatus();
    await _logger.LogAsync("Current Status: " + status.description()); //I don't want to wait for this
    return status;
}

会像这样使用可丢弃的工作吗?:

Public UserStatus GetUserStatus(){
    var status = GenerateStatus();
    _ = _logger.LogAsync("Current Status: " + status.description()); //will this await?
    return status;
}

在最后一种情况下,GetUserStatus()是等待LogAsync()还是在调用后立即继续?


共1个答案

匿名用户

简单的答案是:是的,您可以使用一个discarable来避免等待logAsync完成。 (从技术上讲,您不需要使用discard,但这样做可以避免编译器警告您不在等待它。)

然而,在你这样做之前,你应该了解实际发生了什么。

spender在注释中所说的是,所有异步方法开始同步运行,并在作用于未完成的task的第一个await时返回。 在像logAsync这样的方法中,每当它将日志条目存储到它要去的任何存储介质(例如,文件系统或数据库)时,就会出现这种情况。 一旦该I/O请求启动,它将返回,GetUserStatus可以继续。 通常情况下,那很好。 I/O请求之前的代码通常非常少。

如果您确实不想等待logAsync中的任何代码,那么您可以将其移动到一个新线程:

_ = Task.Run(() => _logger.LogAsync("Current Status: " + status.description()));

然而,这并不是免费的(有开销)。 如果这是一个ASP.NET应用程序,这不是一个好主意,因为ASP.NET的线程数量有限,因此,根据应用程序的负载,它可能会使您接近达到最大值。 在我看来,这不值得。 我不认为你会得到什么。

危险

这整个想法叫做“火了就忘了”。 “忘记”是你需要权衡的危险。 因为你忘记了它,你没有办法知道它是否成功完成。 例如,这可能是一个数据库错误,但在ASP.NET的情况下,如果重新启动应用程序池,IIS将在知道所有HTTP请求都已处理完毕后终止该进程。 它不知道是否有任何后台作业在运行(就像一个fire and forget任务)。 所以它有被杀死的可能。

日志记录可能会失败,你永远不会知道。 当然,99.9999%的情况下都是好的。 但如果您能够容忍任何日志消息在您完全不知道的情况下丢失,则只有您才能回答。

如果你想要降低这种风险,还有其他的处理方法,可以用“以后再做”来形容,而不是“解雇然后忘记”。 Hangfire是一个有助于解决这一问题的库(我从未使用过它,但我看到有人讨论过它)。

相关问题