即发即弃的方法
本文关键字:方法 | 更新日期: 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
而不是 ContinueWith
。 ContinueWith
有一个令人惊讶的默认调度程序(如我的博客所述),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);
}