关于C#/Unity中单例的探讨 📅 发布时间:2026/7/7 3:38:16 👁️ 浏览次数: C#原生类非MonoBehaviourC# 规范规定静态构造函数也就是初始化静态字段的代码由 CLR公共语言运行时保证只执行一次并且是线程安全的。当你第一次访问一个类的静态成员如Instance属性时CLR 会做以下事情检查该类的静态构造函数是否已经执行过。如果没执行过CLR 会自动加锁内部实现比普通的lock更高效。在这个锁的保护下执行静态字段的初始化代码。初始化完成后释放锁。之后所有线程访问该静态字段直接读取内存中的值不需要再加锁。经典线程安全写法利用静态构造函数public class SingletonT where T : class, new() { // 1. 私有静态字段 private static readonly T _instance; // 2. 静态构造函数C# 保证线程安全 static Singleton() { _instance new T(); // 在这里初始化 } // 3. 公共静态属性 public static T Instance { get { return _instance; } } }为什么这个 100% 线程安全因为_instance的赋值发生在static Singleton()构造函数中而 CLR 保证了这个构造函数在多线程环境下只会被调用一次。哪怕 100 个线程同时第一次访问InstanceCLR 也会让它们排队只有一个线程能执行new T()其他线程等待初始化完成后直接拿结果。但是这样的写法不是懒汉模式只要代码中任何地方引用了这个类哪怕只是定义了一个变量SingletonMyClass x;静态构造函数就会立即执行创建实例。如果这个类很重会影响启动速度。简洁写法private static T _instance; public static T Instance { get _instance ?? new T(); private set; }在现代 .NET.NET Framework 4.0 / .NET Core / .NET 5中对于引用类型??操作符的赋值部分是原子的且具有内存屏障Memory Barrier所以这种写法基本上是安全的。原理分析原子性对于引用类型class赋值操作写引用在硬件层面是原子的一次 CPU 指令就能完成。内存屏障C# 编译器和 JIT 编译器会在这里插入内存屏障确保线程 A 创建的对象的内存写入构造函数执行先于对_instance的赋值。线程 B 读取_instance时能看到线程 A 写入的完整对象不会看到半初始化的对象。竞态条件Race Condition虽然??很巧妙但它不是一个不可分割的原子操作。它实际上分两步读取_instance检查是否为 null。如果为 null执行new T()并赋值。极端情况线程 A 读到 null正准备new T()此时线程 B 也读到 null因为 A 还没赋值线程 B 也new T()并赋值。然后线程 A 也new T()并覆盖了 B 的值。后果会创建两个对象但只有一个被存到_instance里另一个变成垃圾。这不算“单例被破坏”因为外部拿到的还是同一个引用但造成了资源浪费多 new 了一次。结论对于引用类型class??足够安全不会出现拿到 null 或者类型转换错误最多就是多创建了一个对象被丢弃在单例场景下通常可接受。对于值类型struct绝对不安全因为装箱和赋值不是原子操作会出现撕裂赋值Tearing。使用LazyT实现最优写法using System; public class SingletonT where T : class, new() { // LazyT 保证线程安全 懒加载 // ExecutionAndPublication 模式确保只有一个线程执行初始化代码 private static readonly LazyT _lazyInstance new LazyT(() new T(), System.Threading.LazyThreadSafetyMode.ExecutionAndPublication); public static T Instance { get { return _lazyInstance.Value; } // 第一次访问 .Value 时才会执行上面的 lambda } }100% 线程安全内部使用了lock和双重检查保证只创建一次。真正的懒加载只有第一次访问.Value时才执行new T()。代码简洁不需要自己写锁逻辑。支持异常缓存如果构造函数抛出异常LazyT会缓存这个异常下次访问直接抛出不会每次都重试。MonoBehaviour因为MonoBehaviour是来自Unity的无法使用new将托管权交给C#所以无法使用上述方法实现单例且MonoBehaviour没有绝对线程安全的单例。using UnityEngine; public class SingletonT : MonoBehaviour where T : MonoBehaviour { private static T _instance; private static readonly object _lock new object(); // 加锁防止多线程竞争虽然Unity主线程但防患未然 public static T Instance { get { // 快速路径如果已经有实例直接返回 if (_instance ! null) return _instance; // 慢速路径去场景里找这一步很慢所以加锁保护 lock (_lock) { if (_instance null) { // 尝试在场景中查找 _instance FindFirstObjectByTypeT(); // Unity 2023 API if (_instance null) { // 如果没找到动态创建 GameObject go new GameObject($[Singleton] {typeof(T).Name}); _instance go.AddComponentT(); } } return _instance; } } } protected virtual void Awake() { // 核心逻辑防止场景中有多个重复的单例 lock (_lock) { if (_instance ! null _instance ! this) { // 如果已经有一个实例了销毁当前这个重复的 Destroy(gameObject); return; } // 设置为当前实例 _instance this as T; // 跨场景不销毁 DontDestroyOnLoad(gameObject); } } }这是在我写的单例的基础上加了锁。但是并非真正线程安全的。因为MonoBehaviour必须用 Unity API 创建而 Unity API只能在主线程用。C# 的静态构造函数/Lazy 可能在后台线程运行直接调用 Unity API 会崩溃。所以 MonoBehaviour 单例只能牺牲一部分线程安全性依赖 Unity 的生命周期Awake和运行时检查来保证逻辑正确性。正确写法是将逻辑层和表现层分离逻辑层public class GameManagerLogic // 普通 C# 类 { private static readonly LazyGameManagerLogic _instance ...; public static GameManagerLogic Instance _instance.Value; public void SaveData() { ... } }表现层public class GameManagerBridge : MonoBehaviour { void Awake() { // 只是确保自己不销毁 DontDestroyOnLoad(gameObject); } public void OnSaveButtonClick() { // 转发调用给纯 C# 单例 GameManagerLogic.Instance.SaveData(); } }
使用大模型中Token用量计算方法Temperature设置详解 ⚡⚡⚡ 新年新文⚡⚡⚡ 文章目录1,Token 用量计算1.1 token概念1.2 离线计算 Tokens 用量2,Temperature(温度) 设置2.1 Temperature概念2.2 核心原理2.3 直观图示2.4 参数设置指南2.5 与其他参数的关系3,Temperature例… 2026/5/17 10:23:22
Matlab Simulink电机FOC观测器模型:龙贝格观测器+PLL无传感器控制 matlab simulink电机foc观测器模型,采用龙贝格观测器PLL进行无传感器控制,其利用 PMSM 数学模型构造观测器模型,根据输出的偏差反馈信号来修正状态变量。 当观测的电流实现与实际电流跟随时,利用估算的反电势进行pll计算转子位置… 2026/5/17 10:23:20
开环模块化多电平换流器仿真(MMC) N=6,连接负载,采用载波移相调制。 可以得到换流器输出... 开环模块化多电平换流器仿真(MMC) N=6,连接负载,采用载波移相调制。 可以得到换流器输出N+1=7电平的相电压波形。 可考虑线路阻抗。 子模块采用半桥结构,旁路电容等参数已设定。 任一… 2026/7/4 12:32:43
商城小程序开发怎么避坑?从商品、订单、支付和售后看选择 商城小程序开发怎么避坑?从商品、订单、支付和售后看选择商城小程序开发最容易踩的坑,不是页面不够漂亮,而是上线后才发现交易链路不完整。商品规格不能灵活维护、订单状态不清楚、支付配置不稳定、优惠活动不好改、会员数据沉淀不下来、售后… 2026/7/7 3:37:33
Cursor Debug误删E盘921GB:Windows Node调试权限与路径安全深度解析 1. 这不是Bug,是Debug模式下的一次“精准误操作”复盘我删掉了E盘921GB文件——不是因为手抖按了ShiftDelete,也不是误点了格式化,而是在Cursor编辑器里点开了那个蓝色的「Debug」按钮,然后看着终端里滚动出一串又一串rm -rf路径&… 2026/7/7 3:37:33
天辛大师漫谈AI时代的如梦令,人类梦想的理解如实记录 天心大师漫谈AI时代的如梦令:梦想的理解也须如实记录跨越时空的哲思,国学视角解构技术迷局的智者,总能在代码与诗行间找到奇妙的共鸣点。当被问及如何用传统词牌《如梦令》观照AI时代,沉吟片刻,缓缓道来:“… 2026/7/7 3:26:31
幼小衔接拼音APP怎么选?适趣拼音为什么适合系统学习拼音? 幼小衔接拼音APP怎么选?适趣拼音为什么适合系统学习拼音? 很多家长到了幼小衔接阶段,都会突然意识到一个问题:拼音不能等到一年级再完全从零开始。 不是说孩子一定要提前学完所有内容,而是如果孩子对拼音完全陌生&… 2026/7/7 3:26:31
Maya动画渲染太慢?一篇文章教你用云渲染,大幅缩短出图时间(附渲染101使用教程) 对于很多做Maya动画的朋友来说,真正耗时间的往往不是建模、绑定或者动画,而是最后的渲染。尤其是使用 Arnold 渲染时,一个镜头可能几百帧,甚至几千帧。如果每一帧需要2~5分钟,那么一个几十秒的动画,往往就需… 2026/7/7 3:26:31
“打卡式”上了一堆系统 + 不断贴身定制开发,别提AI Ready了,反欠了一身技术债 For:CIO、CTO、数字化负责人、先进制造企业CEO 从一家先进制造企业的SAP BTP实践,看复杂制造企业如何走出“接口堆系统”的技术债 核心观点|复杂制造企业的AI Ready,往往不是从模型开始,而是从“干净核心、可控扩展、现… 2026/7/7 3:22:31
Acunetix v24.8 深度解析:DAST漏洞扫描器核心原理与DevSecOps实践 1. 项目概述:Acunetix v24.8 高级版漏洞扫描器深度解析作为一名在网络安全领域摸爬滚打多年的老兵,我深知一款趁手的“兵器”对于安全测试工作意味着什么。今天要聊的,就是Web应用安全测试领域里一个响当当的名字——Acunetix。特别是其v24.8… 2026/7/7 0:01:11
如何3步搞定加密视频下载:跨平台资源嗅探与解密工具终极指南 如何3步搞定加密视频下载:跨平台资源嗅探与解密工具终极指南 【免费下载链接】res-downloader 视频号、小程序、抖音、快手、小红书、直播流、m3u8、酷狗、QQ音乐等常见网络资源下载! 项目地址: https://gitcode.com/GitHub_Trending/re/res-downloader 你是… 2026/7/7 0:03:13
Jailhouse-gui可视化管理工具:让多核处理器分区变得简单高效 Jailhouse-gui可视化管理工具:让多核处理器分区变得简单高效 【免费下载链接】Jailhouse-gui A graphical user interface (GUI) tool for configuring and managing Jailhouse, a Linux-based hypervisor for partitioning multicore processors into isolated cel… 2026/7/7 0:03:13
6个月转型AI工程师:实战路径与核心技能 1. 项目概述:6个月转型AI工程师的可行性路径在2023年大模型技术爆发的背景下,AI工程师岗位需求同比增长217%(LinkedIn数据)。不同于传统算法工程师需要3-5年培养周期,现代AI工程师更侧重工程化落地能力。我在硅谷科技公… 2026/7/6 8:43:22
TPAFE0808与PIC18F87K22的多通道信号采集方案 1. 项目背景与核心需求在工业自动化、医疗设备和科研仪器等领域,多通道信号采集与系统监测是基础且关键的技术需求。传统方案往往面临通道数量不足、信号调理复杂、系统集成度低等问题。TPAFE0808作为一款8通道模拟前端芯片,与PIC18F87K22微控制器的组合… 2026/7/6 7:29:49
STC3115与PIC18LF26K80构建高精度电池管理系统 1. STC3115与PIC18LF26K80在电池管理系统中的核心价值在现代电子设备中,电池管理系统(BMS)的重要性不亚于设备的核心处理器。STC3115作为一款高精度电池电量监测IC,与PIC18LF26K80微控制器的组合,构成了一个既能精确监控又能智能管理的完整解… 2026/7/6 7:29:51