Skip to content

🧠 一、Fire-and-forget 的代码示例

🔸 示例 1:直接触发异步任务,不 await

csharp
_ = Task.Run(async () => await DoWorkAsync());

🔸 示例 2:事件中触发异步任务

csharp
private void OnSomethingHappened()
{
    _ = HandleSomethingAsync();
}
  • 注意 _ = 只是为了抑制编译器警告(忽略返回值),等同于“我知道你是个 Task,但我不关心你何时完成”。

🌟 二、Fire-and-forget 的使用场景和优势

使用场景说明
事件处理(如 FileSystemWatcher、按钮点击)异步任务不能 await,又不希望阻塞主线程
非关键任务(如记录日志、发送通知)不影响主流程,哪怕失败也不重要
后台任务把任务抛给后台线程执行
启动异步执行逻辑主流程继续走,不等待返回

✅ 三、Fire-and-forget 的优点

优点说明
不阻塞当前线程保持 UI 响应、主线程不卡死
简单方便不需要整个调用链都变成异步
适合后台处理对结果不敏感的任务可以快速启动处理
防止死锁.Result.GetAwaiter().GetResult() 安全

⚠️ 四、Fire-and-forget 的风险和注意事项

问题说明解决方法
❗异常被吞掉没有 await,异常没人处理,可能导致进程崩溃必须用 try-catch 包裹逻辑
❗任务未完成程序退出时任务可能还在运行使用后台任务队列或 BackgroundService 处理更可靠
❗资源泄漏异步逻辑里未释放资源,外部无感知加强内部资源管理
❗并发问题多个任务 fire-and-forget 同时运行,可能冲突需要任务节流、锁、状态检查等机制

💡 五、改良建议(Fire-and-forget 的安全写法)

csharp
_ = Task.Run(async () =>
{
    try
    {
        await SomeOperationAsync();
    }
    catch (Exception ex)
    {
        Logger.LogError(ex, "Fire-and-forget task failed.");
    }
});

或者写成一个通用的工具方法:

csharp
public static void SafeFireAndForget(Func<Task> action, ILogger logger)
{
    _ = Task.Run(async () =>
    {
        try
        {
            await action();
        }
        catch (Exception ex)
        {
            logger?.LogError(ex, "Unhandled exception in fire-and-forget task.");
        }
    });
}

使用:

csharp
SafeFireAndForget(() => TriggerJob(CurrentJob), Logger);

✅ 总结

关键词内容
定义启动异步任务而不等待其结果
优点快速、非阻塞、适合轻量操作
风险异常被吞、任务没完成、资源泄漏
最佳实践try-catch 包裹,记录异常日志,限制任务频率

如你希望确保 fire-and-forget 任务不被丢失能顺序执行,可以进一步使用 .NET BackgroundServiceChannel<T> 实现“任务队列”的模式。