【问题标题】:Use cases for async void methods, revisited重新审视异步 void 方法的用例
【发布时间】:2013-11-02 23:03:41
【问题描述】:

我知道 best practice is to avoid async void 方法适用于异步事件处理程序以外的任何东西,而 strong expert opinion 则适用于其他用例。然而,我刚刚参与了一个简短的discussionasync void 方法的有用性,我有几个问题:

  • 框架如何跟踪待处理的async void 方法,包括事件处理程序?有没有办法获取它们的当前列表或取消它们(已编辑:可能通过安装自定义SynchronizationContext 进行跟踪)?
  • 它们对即发即弃的日志记录场景有用吗?我认为它们实际上可能有用,只要在方法开始时保留正确的时间戳,同时它仍然同步执行。

【问题讨论】:

    标签: c# .net asynchronous task-parallel-library async-await


    【解决方案1】:

    框架如何跟踪待处理的 async void 方法,包括事件处理程序?

    框架没有做任何特殊的事情来跟踪异步 void 方法。它们就像任何其他异步方法一样。

    另外,您的方法要么有正确的签名,要么没有;事件处理程序不关心并且没有逻辑来专门检测或使用异步。

    自定义调度程序将能够跟踪正在运行的任务,但不知道是否来自 async void 方法的任何具体知识。无论如何,我认为这不是正确的解决方案——如果您发现自己需要跟踪异步 void 方法,则需要重新考虑您的设计。

    它们对即发即弃的日志记录场景有用吗?我认为它们实际上可能是,只要保留正确的时间戳

    不知道你说的时间戳是什么意思?

    Async void 适用于调用者永远不需要关心方法调用的结果或在其他地方被通知结果的任何方法。这些情况应该非常罕见。

    即发即弃可能就是这样一种情况,尽管我觉得人们经常滥用即发即弃,最终只是对自己隐藏了重要的错误。

    【讨论】:

    • 通过日志记录上下文中的时间戳,我只是指调用方法时的当前时间,应该进入日志。
    • 与任何其他异步方法一样,直到您的第一个“等待”的所有代码都将同步执行。因此,他们会为此目的而工作。不过,我建议不要将即发即弃用于跟踪记录以外的任何事情。
    • 我同意。否则,一个进程可能会在它崩溃之前有一个未保存的日志条目队列,特别是如果它正在登录到远程服务器。
    【解决方案2】:

    关于日志记录场景,这里有两种场景,async-void 适合第一种,但不太适合第二种。

    1) 记录长时间运行的操作的结果:

    public static async void LogCompletion(Task operation, string title)
    {
        try
        {
            await operation.ConfigureAwait(false);
            Log.Info($"{title} completed succesfully");
        }
        catch (Exception ex)
        {
            Log.Error($"{title} failed", ex);
        }
    }
    

    这种用法类似于异步事件处理程序,因为异步操作的完成在概念上类似于引发事件。所以这个方法本质上是“处理”特定任务的完成“事件”。将此 async-void 方法转换为 async Task LogCompletionAsync 方法不会带来很多好处。确实,LogCompletion 方法中的异常会使进程崩溃,但发生异常的唯一可能性是Log.Error 抛出。但是,如果您的日志框架开始抛出异常,那么您的应用程序无论如何也不会长期存在。而且越早了解越好,尽快开始寻找更好的日志框架。

    2) 日志记录本身:

    public static async void Log(string message)
    {
        try
        {
            await File.AppendAllTextAsync(GetLogFilePath(),
                DateTime.Now.ToString() + " " +  message + "\r\n");
        }
        catch { }
    }
    

    这种用法类似于以即发即弃的方式调用异步方法。尽管 async-void 的使用并不可怕,但它是一种非常原始且不复杂的实现日志记录的方式。并且首先尝试实现它是非常不可取的,因为那里有 many 高质量的实现,免费提供。

    【讨论】:

      【解决方案3】:

      它们对于即发即弃的日志记录场景有用吗?

      从概念上讲,我会这么说,但是如果一个任务有一个异常,并且没有通过等待它或访问它的异常属性来处理它,而这在异步 void 方法中不会发生,它将破坏你的应用程序。 所以我会避免这种情况。

      【讨论】:

      • 从 .NET 4.5 开始,默认功能会忽略任何未处理的异常,请参阅 here。您希望避免这种情况是 100% 正确的,但是在“即发即弃”的情况下,这意味着您不关心会发生什么,即使那件事出错了,这可能是为什么默认的理由的一部分行为已更改。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-18
      • 1970-01-01
      • 2011-07-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多