当前位置: 首页 > news >正文

[C#] 多线程单例子,分为阻塞型和分阻塞型, 在unity里的应用

在单例中使用多线程时,需要注意以下几点:

  • 线程安全:在多线程环境下,单例对象可能被多个线程同时访问,因此需要确保单例的线程安全,避免出现数据竞争等问题。

  • 对象创建:如果在单例对象的构造函数中启动了新的线程,那么可能会在单例对象还没有完全创建完成时就开始执行线程。因此,在创建单例对象时需要考虑到线程的启动时机,可以使用懒汉式的延迟加载方式,在需要使用单例对象时再进行初始化。

  • 生命周期管理:如果在单例对象中启动了线程,那么需要考虑线程的生命周期管理,避免线程一直运行导致资源泄漏等问题。可以在单例对象的析构函数中停止线程,或者提供额外的接口供外部调用停止线程。

以下是一个在单例中使用多线程的示例代码:


public class Singleton
{private static Singleton instance = null;private static readonly object padlock = new object();private Thread workerThread;private bool stopWorkerThread = false;public static Singleton Instance{get{lock (padlock){if (instance == null){instance = new Singleton();}return instance;}}}private Singleton(){workerThread = new Thread(WorkerThreadMethod);workerThread.Start();}private void WorkerThreadMethod(){while (!stopWorkerThread){// Do some work...}}public void StopWorkerThread(){stopWorkerThread = true;}~Singleton(){StopWorkerThread();}
}

在这个例子中,Singleton 是一个单例类,它在构造函数中启动了一个工作线程,并且提供了一个 StopWorkerThread 接口用于停止工作线程。在 Singleton 的析构函数中会调用 StopWorkerThread 接口来停止工作线程,确保线程的生命周期管理。在使用 Singleton 时,可以通过 Singleton.Instance 来获取单例对象,并且可以调用 StopWorkerThread 接口来停止工作线程。

非阻塞型

将GetInstance()的返回类型从Task改为UniTask,这是Unity针对异步编程所提供的更高效的API。
将_instance声明为UniTaskCompletionSource类型,并在Initialize()方法完成后使用TrySetResult()方法将结果赋值给_instance。
在Instance属性中使用AsyncLazy类型来实现延迟初始化,并确保多个线程安全地访问单例。
下面是修改后的代码示例:

using Cysharp.Threading.Tasks;public abstract class SingletonTask<T> where T : SingletonTask<T>, new()
{private static readonly AsyncLazy<T> _instance = new AsyncLazy<T>(async () =>{var instance = new T();await instance.InitializeAsync();return instance;});public static UniTask<T> InstanceAsync => _instance.Value;protected virtual UniTask InitializeAsync(){return UniTask.CompletedTask;}
}

这里我们使用了AsyncLazy来延迟初始化单例,并将Initialize()方法改为InitializeAsync(),返回UniTask类型。注意到InitializeAsync()方法是虚方法,方便子类进行实现。

使用时,可以通过调用InstanceAsync属性来获取单例,例如:


public class GameManager : SingletonTask<GameManager>
{private int _score = 0;public void AddScore(int score){_score += score;}protected override UniTask InitializeAsync(){Debug.Log("Game manager initialized.");return UniTask.CompletedTask;}
}// 在其他地方获取GameManager单例
GameManager.InstanceAsync.ContinueWith(gameManager => {gameManager.AddScore(100);
});

这里通过ContinueWith方法来在获取单例后执行添加分数的操作,而不需要等待单例初始化完成。

阻塞型

如果需要等待单例初始化完成,可以在获取单例的时候返回一个 Task 对象,并在单例初始化完成后 Task 对象得到通知。具体的实现可以参考下面的代码:


public abstract class SingletonTask<T> where T : SingletonTask<T>, new()
{private static readonly object padlock = new object();private static T _instance;private static TaskCompletionSource<T> _tcs;public static async Task<T> GetInstanceAsync(){if (_instance != null){return _instance;}if (_tcs == null){_tcs = new TaskCompletionSource<T>();}await _tcs.Task;return _instance;}protected SingletonTask(){lock (padlock){if (_instance != null){throw new InvalidOperationException("Cannot create multiple instances of singleton.");}_instance = this as T;_tcs?.TrySetResult(_instance);}}public abstract Task Initialize();
}

在上面的代码中,GetInstanceAsync 方法返回一个 Task 对象,如果单例已经初始化完成,则直接返回单例;否则创建一个 TaskCompletionSource 对象 _tcs 并返回其 Task 属性。当单例初始化完成时,调用 _tcs.TrySetResult(_instance) 方法,通知等待该 Task 的代码,单例已经初始化完成。具体使用方式如下:


public class MySingleton : SingletonTask<MySingleton>
{private MySingleton(){}public override async Task Initialize(){// 初始化代码await Task.Delay(1000);}
}// 获取单例,并等待初始化完成
var instance = await MySingleton.GetInstanceAsync();

上述代码会等待 MySingleton 的初始化完成,然后返回单例对象。

相关文章:

[C#] 多线程单例子,分为阻塞型和分阻塞型, 在unity里的应用

在单例中使用多线程时&#xff0c;需要注意以下几点&#xff1a; 线程安全&#xff1a;在多线程环境下&#xff0c;单例对象可能被多个线程同时访问&#xff0c;因此需要确保单例的线程安全&#xff0c;避免出现数据竞争等问题。 对象创建&#xff1a;如果在单例对象的构造函数…...

使用MAT进行内存分析,并找到OOM问题

前言 在处理一次现场问题时&#xff0c;发现服务还在运行&#xff0c;但是出现假死情况&#xff0c;后通过分析GC日志以及使用MAT分析确定问题是内存溢出OutOfMemery(OOM)&#xff1b;这里只记录MAT分析学习过程,最近工作忙&#xff0c;补记录。 GC日志分析 首先&#xff0c;如…...

初识Python

目录初识Python1.Python简介Python的优缺点Python的应用领域2.安装Python解释器Windows环境Linux环境macOS环境3.运行Python程序确认Python的版本编写Python源代码运行程序代码中的注释4.Python开发工具IDLE - 自带的集成开发工具IPython - 更好的交互式编程工具Sublime Text -…...

tmux终端复用软件

一、安装[rootpool-100-1-1-159 test]# yum install tmux [rootpool-100-1-1-159 test]# yum search tmux Repository extras is listed more than once in the configuration Last metadata expiration check: 0:33:52 ago on Fri 03 Mar 2023 09:10:34 AM CST.Name Exactly M…...

IO详解(文件,流对象,一些练习)

目录 文件 文件概念 文件的路径 路径有俩种表示风格 文件类型 如何区分文本文件还是二进制文件? java对文件的操作 File类中的一些方法 流对象 流对象的简单概念 java标准库的流对象 1.字节流,(操作二进制数据的) 2.字符流 (操作文本数据的) 流对象最核心的四个…...

SpringCloud全家桶— — 【1】eureka、ribbon、nacos、feign、gateway

SpringCloud全家桶— — 组件搭建 1 Eureka 1.1 Eureka-server 创建eureka-server的SpringBoot项目 ①导入依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId…...

【线程安全篇】

线程安全之原子性问题 x &#xff0c;在字节码文件中对应多个指令&#xff0c;多个线程在运行多个指令时&#xff0c;就存在原子性、可见性问题 赋值 多线程场景下&#xff0c;一个指令如果包含多个字节码指令&#xff0c;那么就不再是原子操作。因为赋值的同时&#xff0c…...

错误:EfficientDet网络出现“No boxes to NMS“并且mAP:0.0的解决方案

近日&#xff0c;在使用谷歌新推出来的一个网络EfficientDet进行目标检测训练自己的数据集的时候&#xff0c;出现了如下错误&#xff1a; 其中项目开源地址是&#xff1a;https://github.com/toandaominh1997/EfficientDet.Pytorch 上面截图中的1和2代表我的类别名称。读者可…...

python的opencv操作记录13——区域生长及分水岭算法

文章目录图像区域基本算法——形态学运算腐蚀与膨胀开运算与闭运算opencv中的形态学运算距离计算——distanceTransform函数连通域连通的定义计算连通域——connectedComponents连通域实验基于区域的分割区域生长算法自定义一个最简单区域生长算法实现区域分割一般区域分割open…...

一文看懂网上下单的手机流量卡为什么归属都是随机的!

最近很多网上下单的小伙伴们心中似乎都有一个疑问。那就是网上很多手机卡、流量卡都不能自选号码和归属地&#xff0c;就算能自选号码&#xff0c;归属地也是随机的而且很多都不会跟你说具体的城市&#xff0c;这是为什么呢&#xff1f;莫非其中有什么不可告人的秘密吗?小伙伴…...

python Pytest生成alluer测试报告的完整教程

1.下载allure包到本地&#xff0c;解压 网上很多资料&#xff0c;这边不提供了 2.配置环境变量 将上面解压后bin文件的路径复制&#xff0c;添加到环境变量Path下 3.验证环境变量配置是否功 在cmd中输入allure&#xff0c;回车 。查看allure是否成功&#xff1a; 4.pyc…...

4-spring篇

ApplicationContext refresh的流程 12个步骤 prepareRefresh 这一步创建和准备了Environment对象&#xff0c;并赋值给了ApplicationContext的成员变量 要理解Environment对象的作用 obtainFreshBeanFactory ApplicationContext 里面有一个成员变量&#xff0c;Beanfactory b…...

提升 Web 应用程序的性能:如何使用 JavaScript 编写缓存服务

缓存是一种重要的优化技术&#xff0c;用于加速数据访问和降低服务器负载。缓存存储经常访问的数据&#xff0c;以便在需要时可以快速检索。在本文中&#xff0c;我们将探索如何使用简单的数据结构在 JavaScript 中编写缓存服务。 编码缓存服务的第一步是定义将用于访问缓存的…...

供应商绩效管理指南:挑战、考核指标与管理工具

管理和优化供应商绩效既关键又具有挑战性。要知道价格并不是一切&#xff0c;如果你的供应商在商定的价格范围内向你开具发票&#xff0c;但服务达不到标准或货物不合格&#xff0c;你也无法达到节约成本的目标。 供应商绩效管理可以深入了解供应商可能带来的风险&#xff0c…...

干货文稿|详解深度半监督学习

分享嘉宾 | 范越文稿整理 | William嘉宾介绍Introduction to Semi-Supervised Learning传统机器学习中的主流学习方法分为监督学习&#xff0c;无监督学习和半监督学习。这里存在一个是问题是为什么需要做半监督学习&#xff1f;首先是希望减少标注成本&#xff0c;因为目前可以…...

信箱|邮箱系统

技术&#xff1a;Java、JSP等摘要&#xff1a;在经济全球化和信息技术飞速发展的今天&#xff0c;通过邮件收发进行信息传递已经成为主流。目前&#xff0c;基于B/S&#xff08;Browser/Server&#xff09;模式的MIS&#xff08;Management information system&#xff09;日益…...

JS数组拓展

1、Array.from Array.from 方法用于将两类对象转为真正的数组&#xff1a; 类似数组的对象,所谓类似数组的对象&#xff0c;本质特征只有一点&#xff0c;即必须有length属性。 因此&#xff0c;任何有length属性的对象&#xff0c;都可以通过Array.from方法转为数组 和 可遍历…...

一道很考验数据结构与算法的功底的笔试题:用JAVA设计一个缓存结构

我在上周的笔试中遇到了这样一道题目&#xff0c;觉得有难度而且很考验数据结构与算法的功底&#xff0c;因此Mark一下。 需求说明 设计并实现一个缓存数据结构: 该数据结构具有以下功能&#xff1a; get(key) 如果指定的key存在于缓存中&#xff0c;则返回与该键关联的值&am…...

(10)C#传智:命名空间、String/StringBuilder、指针、继承New(第10天)

内容开始多了&#xff0c;慢品慢尝才有滋味。 一、命名空间namespace 用于解决类重名问题&#xff0c;可以看作类的文件夹. 若代码与被使用的类&#xff0c;与当前的namespace相同&#xff0c;则不需要using. 若namespace不同时&#xff0c;调用的方法&#xff1a…...

基于Jetson Tx2 Nx的Qt、树莓派等ARM64架构的Ptorch及torchvision的安装

前提 已经安装好了python、pip及最基本的依赖库 若未安装好点击python及pip安装请参考这篇博文 https://blog.csdn.net/m0_51683386/article/details/129320492?spm1001.2014.3001.5502 特别提醒 一定要先根据自己板子情况&#xff0c;找好python、torch、torchvision的安…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

嵌入式学习笔记DAY33(网络编程——TCP)

一、网络架构 C/S &#xff08;client/server 客户端/服务器&#xff09;&#xff1a;由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序&#xff0c;负责提供用户界面和交互逻辑 &#xff0c;接收用户输入&#xff0c;向服务器发送请求&#xff0c;并展示服务…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

spring Security对RBAC及其ABAC的支持使用

RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型&#xff0c;它将权限分配给角色&#xff0c;再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...

游戏开发中常见的战斗数值英文缩写对照表

游戏开发中常见的战斗数值英文缩写对照表 基础属性&#xff08;Basic Attributes&#xff09; 缩写英文全称中文释义常见使用场景HPHit Points / Health Points生命值角色生存状态MPMana Points / Magic Points魔法值技能释放资源SPStamina Points体力值动作消耗资源APAction…...

13.10 LangGraph多轮对话系统实战:Ollama私有部署+情感识别优化全解析

LangGraph多轮对话系统实战:Ollama私有部署+情感识别优化全解析 LanguageMentor 对话式训练系统架构与实现 关键词:多轮对话系统设计、场景化提示工程、情感识别优化、LangGraph 状态管理、Ollama 私有化部署 1. 对话训练系统技术架构 采用四层架构实现高扩展性的对话训练…...

【Vue】scoped+组件通信+props校验

【scoped作用及原理】 【作用】 默认写在组件中style的样式会全局生效, 因此很容易造成多个组件之间的样式冲突问题 故而可以给组件加上scoped 属性&#xff0c; 令样式只作用于当前组件的标签 作用&#xff1a;防止不同vue组件样式污染 【原理】 给组件加上scoped 属性后…...