【问题标题】:Best practices to Fire and Forget an async method to send non essential metric [closed]触发并忘记发送非必要指标的异步方法的最佳实践[关闭]
【发布时间】:2020-05-15 13:26:58
【问题描述】:

等待发送非必要指标对我来说毫无意义,因为它会在每次调用后端 dotnet 核心服务时增加延迟(等待服务器的响应),每次客户端调用可能会发生多次.尽管如此,在失败时记录错误很重要(不过我不需要抛出它,因为指标的失败不应该影响服务)。

我发现了多种方法。以下是以 FireAndForget 方式调用的方法:

public async Task FireAndForget()
{
    try{ await sendTheMetric(); } // Try catch on the await, to log exceptions
    catch(Exception e){ logger.debug(e) }
}

方法一:去掉await。

FireAndForget(); // No await in front of the call

方法 2:对我来说似乎类似于方法 1,因为我们不等待 Task.Factory.StartNew 调用。

Task.Factory.StartNew(async () => await MyAsyncTask());

方法 3:作为工作项在 ThreadPool 上排队。

ThreadPool.QueueUserWorkItem(async o => await FireAndForget());

我很难找到应该将哪个用于 Fire 并忘记调用来发送非必要指标。我的目标是在每次发送指标时不增加对我的服务的每次调用的延迟。当度量标准发送失败时记录错误很重要,但它永远不应该重新抛出。线程上下文对于任务的执行并不重要。任务应该总是,或者至少几乎总是,被完成。

哪一项是满足需求的最佳实践?还是都一样?

注意:我没有包含async void,因为它看起来有风险(如果发生异常,它可能会崩溃,因为没有任务会包装它)。

【问题讨论】:

  • 我会使用 LoggingService 将您的指标缓存在队列中,立即返回您的业务逻辑,并在后台的工作线程中干净地处理它们(所以当出现问题时,您可以处理它完全封装在一个中心点)。但恐怕你的问题没有明确的答案,而且非常基于意见。
  • 我认为“写日志”不应该在线程池中排队。它应该始终按照它的生产顺序。就像弗兰兹所说,日志服务应该处理它。 (比如排队)等
  • 我通常会等待这些寻找的任务,这样异常就不会落空。否则UnobservedTaskException 会使我的程序崩溃。但是,在您的情况下,您正在任务中捕获异常,因此丢弃它是有意义的。但是当您记录到文件并希望它是线程安全的时,您必须等待任务,捕获异常并记录它
  • 如何将 Func 添加到 BlockingCollection 中,然后由单独的线程读取并处理?

标签: c# asynchronous .net-core async-await fire-and-forget


【解决方案1】:

希望这不会使水变得浑浊,但有第 4 个选项(根据我的阅读)正在成为更受欢迎的选项。

从 C#7.0 开始,您可以选择对此类实例使用丢弃。

_ = FireAndForget();

这实质上表明不需要该任务,因此不需要返回值。似乎比方法 1 更推荐,因为这意味着您明确指出它是“一劳永逸”(这不那么模棱两可,而且看起来不像是编码错误)。

【讨论】:

  • 我个人不喜欢这种语法,但我认为这是目前的最佳实践,如果你不丢弃结果,像 resharper 这样的代码分析器甚至会显示警告
  • 发现很少有人提出这个建议,但meziantou.net/fire-and-forget-a-task-in-dotnet.htm 有一个有趣的实现
【解决方案2】:

Method2 似乎也不错,变化不大

      Task.Factory.StartNew(async () => await MyAsyncTask())
        .ContinueWith(t => {
            switch (t.Status)
            {
                case TaskStatus.Created:
                case TaskStatus.WaitingForActivation:
                case TaskStatus.WaitingToRun:
                case TaskStatus.Running:
                case TaskStatus.WaitingForChildrenToComplete:
                case TaskStatus.RanToCompletion:
                    break;
                case TaskStatus.Canceled:
                case TaskStatus.Faulted:
                    //Log error message if any 
                    break;
                default:
                    break;
            }
        });

这样,您仍然可以触发并忘记记录错误消息(如果有)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-01
    • 2014-12-06
    • 2018-07-27
    • 1970-01-01
    • 2020-04-25
    • 2016-06-21
    • 1970-01-01
    • 2015-05-13
    相关资源
    最近更新 更多