即发即弃的方法

本文关键字:方法 | 更新日期: 2023-09-27 17:56:43

与此答案相关,

如果我真的想"触发并忘记"一个返回任务的方法,并且(为简单起见)让我们假设该方法预计不会引发任何异常。我可以使用答案中列出的扩展方法:

public static void Forget(this Task task)
{
}

使用此方法,如果Task的操作中存在导致引发异常的错误,则当引发意外异常时,异常将被吞噬并且不被注意。

问题:在这种情况下,扩展方法的形式是否更合适:

public static async void Forget(this Task task)
{
    await task;
}

因此,编程错误会引发异常并升级(通常会使进程停机)。

对于具有预期(和可忽略)异常的方法,该方法

需要变得更加详细(顺便说一句,关于如何构造此方法的版本的任何建议,该版本将采用可接受和可忽略的异常类型列表?

即发即弃的方法

这取决于你想要的语义。如果您想确保注意到异常,那么是的,您可以await任务。但在这种情况下,它并不是真正的"即发即弃"。

真正的"即发即弃"——从某种意义上说,你不关心它何时完成,也不关心它是否成功完成或有错误——是非常罕见的。

编辑:

对于处理异常:

public static async void Forget(this Task task, params Type[] acceptableExceptions)
{
  try
  {
    await task.ConfigureAwait(false);
  }
  catch (Exception ex)
  {
    // TODO: consider whether derived types are also acceptable.
    if (!acceptableExceptions.Contains(ex.GetType()))
      throw;
  }
}

请注意,我建议使用 await 而不是 ContinueWithContinueWith有一个令人惊讶的默认调度程序(如我的博客所述),Task.Exception会将实际的异常包装在AggregateException中,使错误处理代码更加繁琐。

在链接的问题中,我最初想在以下上下文中使用static void Forget(this Task task)

var task = DoWorkAsync();
QueueAsync(task).Forget();
// ...
async Task QueueAsync(Task task)
{
    // keep failed/cancelled tasks in the list
    // they will be observed outside
    _pendingTasks.Add(task);
    await task;
    _pendingTasks.Remove(tasks)
}

它看起来很棒,但后来我意识到 _pendingTasks.Add/_pendingTasks.Remove 可能引发的致命异常将被忽视和丢失,这并不好。

所以我简单地QueueTask async void方法,它的本质是:

var task = DoWorkAsync();
QueueAsync(task);
// ...
async void QueueAsync(Task task)
{
    // keep failed/cancelled tasks in the list
    // they will be observed outside
    _pendingTasks.Add(task);
    try
    {
        await task;
    }
    catch
    {
        return;
    }
    _pendingTasks.Remove(tasks)
}

尽管我不喜欢空catch {},但我认为这是有道理的。

在这种情况下,保持async Task QueueAsync()并使用您建议的async void Forget(this Task task)将是矫枉过正,IMO。

是的,如果您对任务是否引发异常感兴趣,那么您需要await结果,但另一方面,这几乎违背了"即发即弃"的目的。

在您想知道是否发生了不好的事情的情况下,建议的方法是使用延续,例如

public static void ForgetOrThrow(this Task task)
{
    task.ContinueWith((t) => {
        Console.WriteLine(t.Exception);
    }, TaskContinuationOptions.OnlyOnFaulted);
}