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

推荐订阅源

Spread Privacy
Spread Privacy
A
Arctic Wolf
T
Threatpost
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
The GitHub Blog
The GitHub Blog
博客园 - 聂微东
Cyberwarzone
Cyberwarzone
博客园 - Franky
V2EX - 技术
V2EX - 技术
The Hacker News
The Hacker News
量子位
TaoSecurity Blog
TaoSecurity Blog
Last Week in AI
Last Week in AI
C
CERT Recently Published Vulnerability Notes
P
Palo Alto Networks Blog
Scott Helme
Scott Helme
D
DataBreaches.Net
T
Troy Hunt's Blog
T
Threat Research - Cisco Blogs
美团技术团队
雷峰网
雷峰网
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
H
Hacker News: Front Page
D
Docker
C
Check Point Blog
G
GRAHAM CLULEY
H
Heimdal Security Blog
IT之家
IT之家
博客园 - 叶小钗
The Cloudflare Blog
H
Help Net Security
M
MIT News - Artificial intelligence
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Security Archives - TechRepublic
Security Archives - TechRepublic
N
Netflix TechBlog - Medium
Apple Machine Learning Research
Apple Machine Learning Research
I
Intezer
P
Proofpoint News Feed
S
Security Affairs
P
Privacy International News Feed
U
Unit 42
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Webroot Blog
Webroot Blog
Google DeepMind News
Google DeepMind News
大猫的无限游戏
大猫的无限游戏
人人都是产品经理
人人都是产品经理
N
News and Events Feed by Topic
Know Your Adversary
Know Your Adversary
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org

博客园 - 世纪末の魔术师

《The Wealth Ladder》读书笔记 项目管理,真正的密码只有一句话 Linq延迟执行陷阱 坚毅,是一种缓慢修理自己的方式 语言的边界,与软件的命运 人类与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() CancellationTokenSource
目的 忽略等待结果 控制任务生命周期
适用场景 轻量、短期任务 长时、可取消任务
是否能终止任务 ❌ 否 ✅ 可以取消
是否捕获异常 ❌ 默认丢弃(除非 .Forget(ResultHandling...)) ✅ 可被 try-catch 捕获
GC 开销 少量(每次创建)

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

最佳做法是:

👉「任务逻辑用 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?

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

🧱 四、实际对比:有无 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));

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

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

🔍 七、进阶:组合式 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