惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
P
Palo Alto Networks Blog
T
ThreatConnect
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
T
True Tiger Recordings
P
Privacy & Cybersecurity Law Blog
B
Blog
IT之家
IT之家
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
C
Comments on: Blog
Microsoft Azure Blog
Microsoft Azure Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
N
News and Events Feed by Topic
NISL@THU
NISL@THU
腾讯CDC
雷峰网
雷峰网
Security Latest
Security Latest
李成银的技术随笔
M
Microsoft Research Blog - Microsoft Research
L
LangChain Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
博客园 - Franky
N
News | PayPal Newsroom
V
V2EX
A
About on SuperTechFans
The Register - Security
The Register - Security
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
Vercel News
Vercel News
WordPress大学
WordPress大学
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
爱范儿
爱范儿
A
Arctic Wolf
L
LINUX DO - 最新话题
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - 世纪末の魔术师

坚毅,是一种缓慢修理自己的方式 语言的边界,与软件的命运 人类与AI协同进化 《坚毅》第一部分读书笔记 用 System.CommandLine 构建工程级 CLI 工具 用 Command 模式构建可扩展的命令行工具 从哎呦”到语言宇宙 ——读《What Is ChatGPT Doing … And Why Does It Work?》 ⏱️ 深入理解定时器中的【时间轮算法】 🚫 为什么「定时器」不应该是线程安全的? C# AOT编译后——调用其类库方法因顺序出错? 光线追踪和球体追踪 八、方法(method) 二十、异常与状态管理(Exception&State Management) 二十八、IO绑定的异步操作(IO-Bound Async) 二十二、CLR寄宿与AppDomain(CLR Hosting and App Domains ) 二十九、原始线程同步构造(Primitive Thread Synchronization Constructs ) 二十六、线程与并发(Thread Basic) 二十七、计算密集型异步操作(Compute-Bound Asynchronous Operations) 二十三、程序集加载与反射(Assembly Loading and Reflection)
UnitTask中的Forget()与 CTS
世纪末の魔术师 · 2025-10-11 · via 博客园 - 世纪末の魔术师

🎯 结论先行:不是“每个异步方法”都要加 CTS

而是「凡是可能长时间存在、或受生命周期控制的任务」才应该加 CTS。

🧩 一、Forget()CTS 的核心区别

目的 忽略等待结果 控制任务生命周期 适用场景 轻量、短期任务 长时、可取消任务 是否能终止任务 ❌ 否 ✅ 可以取消 是否捕获异常 ❌ 默认丢弃(除非 .Forget(ResultHandling...)) ✅ 可被 try-catch 捕获 GC 开销 无 少量(每次创建)
对比项 .Forget() CancellationTokenSource

🧭 二、两者协同工作的黄金范式

最佳做法是:

👉「任务逻辑用 CTS 控制生命周期」,
👉「调用者决定是否 await.Forget()」。

示例:

private CancellationTokenSource cts;

private void Start()
{
    cts = new();
    LoadSceneAsync(cts.Token).Forget(); // 调用方选择忽略
}

private async UniTask LoadSceneAsync(CancellationToken token)
{
    await UniTask.Delay(5000, cancellationToken: token);
    Debug.Log("场景加载完成");
}

private void OnDestroy()
{
    cts.Cancel(); // 防止场景切换或对象销毁时任务泄漏
}

💡 解释:

  • LoadSceneAsync 不关心是否被 await;

  • cts.Token 决定任务是否继续;

  • Forget() 让调用方不阻塞,但内部仍受控。

⚙️ 三、什么时候“必须”加 CTS?

⏳ 延迟逻辑(如 Delay, WaitUntil) ✅ 是 避免对象销毁后继续等待 🔄 循环任务(如 AI 行为、监控) ✅ 是 必须能中断 🌐 异步加载(网络/资源) ✅ 是 用户可能切场景、关闭窗口 ⚡ 短期逻辑(如 UI 动画) ❌ 可省略 生命周期很短 🧩 同步逻辑(例如计算) ❌ 不必要 一次执行完即结束 🧠 Editor 模式(非运行时) ✅ 推荐 编辑器中生命周期不稳定
场景 是否建议加 CTS 原因

🧱 四、实际对比:有无 CTS 的差异

❌ 无 CTS(任务泄漏)

private async void Start()
{
    await UniTask.Delay(5000);
    Debug.Log("对象早就销毁了,但我还在执行!");
}

✅ 有 CTS(安全终止)

private async void Start()
{
    var token = this.GetCancellationTokenOnDestroy();
    await UniTask.Delay(5000, cancellationToken: token);
    Debug.Log("对象没销毁才会执行到这里");
}

💡 说明:

GetCancellationTokenOnDestroy() 是 UniTask 为 MonoBehaviour 提供的神器,它会在对象销毁时自动取消。

🧩 五、关于 Forget() 的使用建议

✅ 推荐写法:

SomeAsync().Forget(); // 我知道它会跑完,不需要返回结果

⚠️ 不推荐写法:

await SomeAsync().Forget(); // 无意义,Forget返回void

🚨 异常丢失问题:

.Forget() 默认吞异常,你可以这样防御:

SomeAsync().Forget(e => Debug.LogException(e));

🧠 六、实战建议(通俗记忆法)

UI 动画 否 ✅ 网络请求 ✅ ❌(要 await 结果) AI 行为循环 ✅ ✅ Editor 扩展操作 ✅ ✅ 一次性逻辑 否 ✅ 场景加载 ✅ ❌(等待完成)
任务类型 是否加 CTS 是否 Forget

🔍 七、进阶:组合式 CTS 管理

你可以用一个父级 Token 控制多个异步任务:

var cts = new CancellationTokenSource();

DoA(cts.Token).Forget();
DoB(cts.Token).Forget();
DoC(cts.Token).Forget();

// 一键取消所有
cts.Cancel();

或者组合多个 Token:

var linked = CancellationTokenSource.CreateLinkedTokenSource(token1, token2);
await UniTask.Delay(1000, cancellationToken: linked.Token);

📈 八、性能注意

  • 创建一个 CTS 的开销 ≈ 40~60B GC Alloc;

  • 所以:
    👉 建议在 MonoBehaviour 级别缓存
    👉 而不是每次 async 方法都 new 一个。

private CancellationTokenSource cts;
private void Awake() => cts = new();
private void OnDestroy() => cts.Cancel();

🧩 九、最佳实践模板(生产级推荐)

public class Example : MonoBehaviour
{
    private CancellationTokenSource _cts;

    private void Awake()
    {
        _cts = new();
    }

    private void OnDestroy()
    {
        _cts.Cancel();
        _cts.Dispose();
    }

    private void Start()
    {
        RunAsync(_cts.Token).Forget();
    }

    private async UniTask RunAsync(CancellationToken token)
    {
        while (!token.IsCancellationRequested)
        {
            await UniTask.Delay(1000, cancellationToken: token);
            Debug.Log("持续工作中...");
        }
    }
}

🧠 记忆口诀

Forget 是“不管结局”,CTS 是“能中断结局”。

  • 只要任务可能「超时、循环、依赖对象存在」,就加 CTS

  • 调用方只关心“我是否等待结果”,就用 Forget