注意:这是一个针对Asp.NetWeb 应用程序领域之外的问题。
一般来说,特别是在涉及库或控制台应用程序时,为了触发并忘记异步方法,只调用异步方法而不await使用它或使用它更好Task.Run吗?
基本上:
public static void Main(){
// Doing
_ = DoSomethingAsync();
// vs.
_ = Task.Run(DoSomethingAsync);
}
private static async Task DoSomethingAsync()
{
...
}
Run Code Online (Sandbox Code Playgroud)
抛开“我是否应该使用即发即弃”的争论和“哪些任务适合即发即弃”的争论:
就我个人而言,我使用 Gerald Barre 的这个好帮手,在 Nick Chapsas 视频博客推荐 - 原文在这里:https ://www.meziantou.net/fire-and-forget-a-task-in-dotnet.htm
有趣的事实:ASP.NET Core SignalR 使用此代码的变体。
public static class TaskExtensions
{
/// <summary>
/// Observes the task to avoid the UnobservedTaskException event to be raised.
/// </summary>
public static void Forget(this Task task)
{
// note: this code is inspired by a tweet from Ben Adams: https://twitter.com/ben_a_adams/status/1045060828700037125
// Only care about tasks that may fault (not completed) or are faulted,
// so fast-path for SuccessfullyCompleted and Canceled tasks.
if (!task.IsCompleted || task.IsFaulted)
{
// use "_" (Discard operation) to remove the warning IDE0058: Because this call is not awaited, execution of the current method continues before the call is completed
// https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/discards?WT.mc_id=DT-MVP-5003978#a-standalone-discard
_ = ForgetAwaited(task);
}
// Allocate the async/await state machine only when needed for performance reasons.
// More info about the state machine: https://blogs.msdn.microsoft.com/seteplia/2017/11/30/dissecting-the-async-methods-in-c/?WT.mc_id=DT-MVP-5003978
async static Task ForgetAwaited(Task task)
{
try
{
// No need to resume on the original SynchronizationContext, so use ConfigureAwait(false)
await task.ConfigureAwait(false);
}
catch
{
// Nothing to do here
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
用法
LogRunningTask().Forget()
Run Code Online (Sandbox Code Playgroud)
一般来说,特别是在涉及库或控制台应用程序时,为了触发并忘记异步方法,是只调用异步方法而不等待它还是使用 Task.Run 更好?
一般来说,最好不要使用即发即忘。
“火而忘记”的意思是:
简而言之,“即发即忘”只适用于极少数的任务。例如,更新缓存。我说大概85%以上的“发射后不管”的代码是错误的-它用火和忘记的代码应该不会是火灾和忘记。
所以我想说最好的解决方案是不使用火而完全忘记。在最起码,你应该露出Task一个表示“后续行动”的地方。考虑将 添加Task到您的返回类型或将其作为属性公开。
采用即发即弃——尤其是在图书馆中——意味着你迫使所有消费者永远不知道何时关闭和退出是安全的。但是,如果您真的想开火而忘,则有几种选择。
A. 一种选择是在没有上下文的情况下调用async void函数。消费应用程序仍然无法确定代码是否/何时完成,但至少不会忽略异常。
B. 另一种选择是在没有上下文的情况下开始任务。此选项具有触发和忘记代码的缺点:忽略异常并且调用代码无法知道它何时完成。
这两个建议都是在没有上下文的情况下开始任务的。有帮助程序可以执行此操作,或者您可以将调用包装起来Task.Run(效率稍低,但效果很好)。
我不建议直接启动任务。虽然这在控制台应用程序中可以正常工作,但它不适用于在提供上下文的情况下可能调用的库。