火和忘记方法

时间:2014-04-04 13:34:49

标签: c# async-await

this answer相关,

如果我真的想要“Fire and Forget”一个确实返回任务的方法,并且(为简单起见)让我们假设该方法不会抛出任何异常。我可以使用答案中列出的扩展方法:

public static void Forget(this Task task)
{
}

使用这种方法,如果Task的操作中存在导致抛出异常的错误,那么当抛出意外异常时,将吞下该异常并且不被注意。

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

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

因此,编程错误会引发异常并升级(通常会导致进程失效)。

在具有预期(和可忽略的)异常的方法的情况下,该方法需要变得更加精细(除此之外,关于如何构造此方法的版本的任何建议将采用可接受和可忽略的列表异常类型?)

3 个答案:

答案 0 :(得分:44)

这取决于你想要的语义。如果您想确保注意到异常,那么您可以await完成任务。但在那种情况下,它并不是真正的,而是忘记了#34;。

真实的"火灾和忘记" - 从某种意义上说,你不关心它何时完成,或者它是否成功完成或是否有错误 - 这种情况极为罕见。

修改

处理例外:

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 has a surprising default scheduler(如我的博客中所述)和Task.Exception将实际异常包装在AggregateException中,使错误处理代码更加繁琐。

答案 1 :(得分:3)

是的,如果您对该任务是否引发异常感兴趣,那么您需要await结果,但另一方面,这几乎违背了" fire的目的&安培;忘记"

在您想知道错误是否发生的情况下,建议的方法是使用延续,例如。

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

答案 2 :(得分:3)

linked question我最初想在以下背景中使用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可能抛出的fatal exceptions将被忽视并丢失,这是不好的。

所以我只是简单地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。