Unity 时间定时调度系统

C# Unity 时间定时调度系统

之前的文章也有写过时间调度系统,但是没有支持异步调度只有回调调度,而且效率和代码可读性不是很好,下面介绍一种更优质的时间调度系统

1.TimerAction

首先需要定义一个时间行为,每次延时后需要干什么,延迟的时间类型是什么都需要使用TimerAction

public sealed class TimerAction : IDisposable
{private static long _id;//每个时间任务的ID生成public long Id;//每个时间任务的IDpublic long Time;//需要等待的时间(只有设置为Repeated的类型才有用)public object CallBack;//定时回调(不同类型的时间调度不同的类型可能是异步可能是委托所以用object类型)public TimerType TimerType;//时间调度类型public static TimerAction Create(){TimerAction timerAction = Pool<TimerAction>.Rent(); // 从引用池中创建时间调度任务减轻GCtimerAction.Id = _id++;return timerAction;}public void Dispose(){Id = 0;Time = 0;CallBack = null;this.TimerType = TimerType.None;Pool<TimerAction>.Return(this);}
}

2.时间调度类型TimerType

public enum TimerType 
{None,OnceTimer,//单次回调委托类型的时间调度OnceWaitTimer,//单次异步时间调度(await/async)RepeatedTimer,//重复的时间调度
}

3.TimerScheduler时间调度器

这个调度器是个单例,单例在此不讲解实现可以看之前的文章有提

(1)获取当前时间

由于是在Unity环境下则使用Unity获取时间的方式,若在服务器或者纯C#的环境该时间的获取需要根据对应时间填入即可

    private long GetNowTime(){return (long)Time.time * 1000;}

(2)执行一个时间调度

    public long OnceTimer(long time, Action action){return OnceTillTimer(GetNowTime() + time, action);}public long OnceTillTimer(long tillTime, Action action){if (tillTime < GetNowTime())Debug.LogError($"new once time too small tillTime:{tillTime} Now:{GetNowTime()}");TimerAction timerAction = TimerAction.Create();timerAction.CallBack = action;timerAction.TimerType = TimerType.OnceTimer;AddTimer(tillTime, timerAction);return timerAction.Id;}
  • 这里说明一下_minTime 这个字段用于辨别最小时间是否达到,如果连最小时间都没有达到就不执行所有时间调度的检测
    private void AddTimer(long tillTime, TimerAction timerAction){_timerActionDic.Add(timerAction.Id, timerAction);_timeIdDic.Add(tillTime, timerAction.Id);if (tillTime < _minTime)_minTime = tillTime;}

(3)执行时间调度检测

    private long _minTime;private readonly Queue<long> _timeOutTimeQueue = new();//tillTimeprivate readonly Queue<long> _timeOutIdsQueue = new();//idprivate readonly Dictionary<long, TimerAction> _timerActionDic = new();//Key: idprivate readonly SortedOneToManyList<long, long> _timeIdDic = new();//Key: tillTime  Value: id
    public void Update(){try{long currTime = GetNowTime();if (_timeIdDic.Count == 0)return;if (currTime < _minTime)return;_timeOutTimeQueue.Clear();_timeOutIdsQueue.Clear();foreach (var (key, value) in _timeIdDic){if (key > currTime){_minTime = key;break;}_timeOutTimeQueue.Enqueue(key);}while (_timeOutTimeQueue.TryDequeue(out long tillTime)){foreach (long timerId in _timeIdDic[tillTime]){_timeOutIdsQueue.Enqueue(timerId);}_timeIdDic.RemoveKey(tillTime);}while (_timeOutIdsQueue.TryDequeue(out long timerId)){if (!_timerActionDic.TryGetValue(timerId, out TimerAction timerAction))continue;_timerActionDic.Remove(timerId);switch (timerAction.TimerType){case TimerType.OnceTimer:{Action action = (Action)timerAction.CallBack;timerAction.Dispose();if (action == null){Debug.LogError("Call Back Is Null");break;}action();}break;case TimerType.RepeatedTimer:{Action action = (Action)timerAction.CallBack;AddTimer(GetNowTime() + timerAction.Time, timerAction);if (action == null){Debug.LogError("Call Back Is Null");break;}action();}break;case TimerType.OnceWaitTimer:{TaskCompletionSource<bool> taskCompletionSource = (TaskCompletionSource<bool>)timerAction.CallBack;timerAction.Dispose();taskCompletionSource.SetResult(true);}break;default:break;}}}catch (Exception ex){Debug.LogError(ex.Message);}}

(4)TimerScheduler完整代码

public class TimerScheduler : Singleton<TimerScheduler>, IUpdateSingleton
{private long _minTime;private readonly Queue<long> _timeOutTimeQueue = new();//tillTimeprivate readonly Queue<long> _timeOutIdsQueue = new();//idprivate readonly Dictionary<long, TimerAction> _timerActionDic = new();//Key: idprivate readonly SortedOneToManyList<long, long> _timeIdDic = new();//Key: tillTime  Value: idprivate long GetNowTime(){return (long)Time.time * 1000;}private void AddTimer(long tillTime, TimerAction timerAction){_timerActionDic.Add(timerAction.Id, timerAction);_timeIdDic.Add(tillTime, timerAction.Id);if (tillTime < _minTime)_minTime = tillTime;}public long OnceTimer(long time, Action action){return OnceTillTimer(GetNowTime() + time, action);}public long OnceTillTimer(long tillTime, Action action){if (tillTime < GetNowTime())Debug.LogError($"new once time too small tillTime:{tillTime} Now:{GetNowTime()}");TimerAction timerAction = TimerAction.Create();timerAction.CallBack = action;timerAction.TimerType = TimerType.OnceTimer;AddTimer(tillTime, timerAction);return timerAction.Id;}public long RepeatedTimer(long time,Action action){if (time <= 0){throw new Exception("repeated time <= 0");}long tillTime = GetNowTime() + time;TimerAction timerAction = TimerAction.Create();timerAction.CallBack = action;timerAction.TimerType = TimerType.RepeatedTimer;timerAction.Time = time;AddTimer(tillTime, timerAction);return timerAction.Id;}public void Remove(long timerId){if (!_timerActionDic.Remove(timerId, out TimerAction timerAction))return;timerAction?.Dispose();}public void Update(){try{long currTime = GetNowTime();if (_timeIdDic.Count == 0)return;if (currTime < _minTime)return;_timeOutTimeQueue.Clear();_timeOutIdsQueue.Clear();foreach (var (key, value) in _timeIdDic){if (key > currTime){_minTime = key;break;}_timeOutTimeQueue.Enqueue(key);}while (_timeOutTimeQueue.TryDequeue(out long tillTime)){foreach (long timerId in _timeIdDic[tillTime]){_timeOutIdsQueue.Enqueue(timerId);}_timeIdDic.RemoveKey(tillTime);}while (_timeOutIdsQueue.TryDequeue(out long timerId)){if (!_timerActionDic.TryGetValue(timerId, out TimerAction timerAction))continue;_timerActionDic.Remove(timerId);switch (timerAction.TimerType){case TimerType.OnceTimer:{Action action = (Action)timerAction.CallBack;timerAction.Dispose();if (action == null){Debug.LogError("Call Back Is Null");break;}action();}break;case TimerType.RepeatedTimer:{Action action = (Action)timerAction.CallBack;AddTimer(GetNowTime() + timerAction.Time, timerAction);if (action == null){Debug.LogError("Call Back Is Null");break;}action();}break;case TimerType.OnceWaitTimer:{TaskCompletionSource<bool> taskCompletionSource = (TaskCompletionSource<bool>)timerAction.CallBack;timerAction.Dispose();taskCompletionSource.SetResult(true);}break;default:break;}}}catch (Exception ex){Debug.LogError(ex.Message);}}public async Task<bool> WaitAsync(long time, CancellationAction cancellationAction = null){return await WaitTillAsync(GetNowTime() + time, cancellationAction);}public async Task<bool> WaitTillAsync(long tillTime, CancellationAction cancellationAction = null){if (GetNowTime() > tillTime)return true;TaskCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>();TimerAction timerAction = TimerAction.Create();long timerId = timerAction.Id;timerAction.CallBack = taskCompletionSource;timerAction.TimerType = TimerType.OnceWaitTimer;void CancelAction(){if (!_timerActionDic.ContainsKey(timerId))return;Remove(timerId);taskCompletionSource.SetResult(false);}bool b;try{cancellationAction?.Add(CancelAction);AddTimer(tillTime, timerAction);b = await taskCompletionSource.Task;}catch(Exception ex){Debug.LogError(ex.Message);return true;}return b;}protected override void Load(int assemblyName){}protected override void UnLoad(int assemblyName){}
}

4.测试

  • 提供一下CancellationAction代码实现,用于一切的取消事件(非时间调度器专属,任何取消操作都可参考)
public sealed class CancellationAction
{private HashSet<Action> _actions = new HashSet<Action>();public bool IsCanel => _actions == null;public void Add(Action action){_actions.Add(action);}public void Remove(Action action){_actions.Remove(action);}public void Cancel(){if (_actions == null)return;foreach (Action action in _actions){try{action?.Invoke();}catch (Exception e){Debug.LogError(e.Message);}}_actions.Clear();_actions = null;}
}
public class TestTimer : MonoBehaviour
{private long repeatedId;private CancellationAction timerCancelAction = new CancellationAction();async void Start(){SingletonSystem.Initialize();AssemblyManager.Initialize();TimerScheduler.Instance.OnceTimer(5000, () =>{Debug.Log("第一个5s后了!!!");});TimerScheduler.Instance.OnceTimer(5000, () =>{Debug.Log("第二个5s后了!!!");});TimerScheduler.Instance.OnceTimer(6000, () =>{Debug.Log("6s后了!!!");});repeatedId = TimerScheduler.Instance.RepeatedTimer(2000, () =>{Debug.Log("每两秒重复运行!!!");});await TimerScheduler.Instance.WaitAsync(3000);Debug.Log("这是第一个3s以后");await TimerScheduler.Instance.WaitAsync(3000);Debug.Log("这是第二个3s以后");bool isCanel = await TimerScheduler.Instance.WaitAsync(5000, timerCancelAction);if (!isCanel){Debug.Log("取消定时");}else{Debug.Log("五秒后执行!!!");}}void Update(){SingletonSystem.Update();if (Input.GetKeyDown(KeyCode.P)){if (!timerCancelAction.IsCanel){timerCancelAction.Cancel();}}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/71349.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器学习处理问题的基本路线

基本路线&#xff1a; 1.搭建环境/数据读入 2.数据分析 3.特征工程 4.建模调参 5.模型融合 异常处理&#xff1a; 通过箱线图&#xff08;或 3-Sigma&#xff09;分析删除异常值&#xff1b;BOX-COX 转换&#xff08;处理有偏分布&#xff09;&#xff1b;长尾截断&…

Redis基本了解

Redis 基于内存进⾏存储&#xff0c;⽀持 key-value 的存储形式&#xff0c;底层是⽤ C 语⾔编写的。 基于 key-value 形式的数据字典&#xff0c;结构⾮常简单&#xff0c;没有数据表的概念&#xff0c;直接⽤键值对的形式完成数据的管理&#xff0c;Redis ⽀持 5 种数据类型…

[machine Learning]推荐系统

其实严格来说推荐系统也是一种监督学习,我们需要根据已有数据进行预测,但是这种训练数据不是单纯的输入和输出问题,所以被归类为"超越监督学习"的一种? 今天去旁听了隔壁专业的机器学习课程,感觉自己的知识确实不是很系统,所以后面会找个机会把前面的代码给补充上.…

Qt打开及创建项目,运行程序(1)

安装之后&#xff0c; 1.文件->新建文件或项目 2.Application->Qt Widgets Application 3.自己设置名称和路径 4.这一步非常非常重要&#xff0c;要选择编译器&#xff0c;&#xff08;MinGW是可以在Qt里用&#xff0c;如果想与VS交互&#xff0c;要选择MSVC&#xff09…

iOS开发Swift-类型转换

1.Int或Double转字符串 let x 20 let y "\(x)" let z String(x)2.Double转Int(去掉小数点后面的) Int(1.9)3.Int转Double Double(1)4.向上转型 class A{//A父类 }class B: A{//B子类继承A }let a A() let b B()b as A //子类转化成父类5.向下转型 class A{//A…

uniapp 路由不要显示#

在Uniapp中&#xff0c;路由默认使用的是hash模式&#xff0c;即在URL中添加#符号。如果你不想在URL中显示#&#xff0c;可以切换为使用history模式。 要在Uniapp中使用history模式&#xff0c;可以按照以下步骤进行操作&#xff1a; 打开manifest.json文件。在"app&qu…

解决charles只能使用30分钟

问题描述 Charles 30分钟会自动关闭&#xff0c;弹出一个弹窗。 解决步骤 1.网上查找后发现是需要注册一下。 2.打开Charles&#xff0c;如图的操作顺序 3.框内输入 Registered Name: https://zhile.io License Key: 48891cf209c6d32bf4 4.重启即可

在css中设计好看的阴影

在css中设计好看的阴影 在本文中&#xff0c;我们将学习如何将典型的盒子阴影转换为更好看的的阴影。 统一角度 当我们想要一个元素有阴影时&#xff0c;会添加box-shadow属性并修改其中的数字&#xff0c;直到满意为止。 问题是&#xff0c;通过像这样单独创建每个阴影&…

vue的第2篇 开发环境vscode的安装以及创建项目空间

一 环境的搭建 1.1常见前端开发ide 1.2 安装vs.code 1.下载地址&#xff1a;Visual Studio Code - Code Editing. Redefined 2.进行安装 1.2.1 vscode的中文插件安装 1.在搜索框输入“chinese” 2.安装完成重启&#xff0c;如下变成中文 1.2.2 修改工作区的颜色 选中[浅色]…

python实现某音自动登录+获取视频数据

前言 Dy这个东西想必大家都用过&#xff0c;而且还经常刷&#xff0c;今天就来用代码&#xff0c;获取它的视频数据 环境使用 Python 3.8 Pycharm 模块使用 requests selenium json re 一. 数据来源分析 1. 明确需求 明确采集网站以及数据内容 网址: https://www.dy.com/…

【LeetCode-中等题】40. 组合总和 II

文章目录 题目方法一&#xff1a;递归回溯去重 题目 本题需要注意的就是去重操作因为nums数组里面的元素可能存在重复&#xff1a; 不重复的版本&#xff1a;【LeetCode-中等题】39. 组合总和 不去重版 方法一&#xff1a;递归回溯去重 参考讲解视频—回溯算法中的去重&#…

深入了解苹果证书及其分类,提升iOS应用开发效率

目录 1. 企业证书 2. 开发者证书 开发证书&#xff1a; 发布证书&#xff1a; 3. 推送证书 4. 分发证书 5. MDM证书 摘要&#xff1a;本文将详细介绍苹果证书的作用及分类&#xff0c;包括企业证书、开发者证书、推送证书、分发证书和MDM证书&#xff0c;帮助开发者了解…

04-Apache Directory Studio下载安装(LDAP连接工具)

1、下载 官网下载Apache Directory Studio 注意Apache Directory Studio依赖于jdk&#xff0c;对jdk有环境要求 请下载适配本机的jdk版本的Apache Directory Studio,下图为最新版下载地址 Apache Directory Studio Version 2.0.0-M16 基于 Eclipse 2020-12&#xff0c;最低要…

C#使用Panel

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System

CVE-2023-20883 拒绝服务攻击

在Spring Boot版本3.0.0-3.0.6、2.7.0-2.7.11、2.6.0-2.6.14、2.5.0-2.5.14和不受支持的旧版本中&#xff0c;如果将Spring MVC与反向代理缓存一起使用&#xff0c;则可能会发生拒绝服务&#xff08;DoS&#xff09;攻击 一、影响范围 Spring Boot 3.0.x版本&#xff1a;3.0.0…

AJAX学习笔记2发送Post请求

AJAX学习笔记1发送Get请求_biubiubiu0706的博客-CSDN博客 继续 AJAX发送POST请求 无参数 测试 改回来 测试 AJAX POST请求 请求体中提交参数 测试 后端打断点 如何用AJAX模拟form表单post请求提交数据呢&#xff1f; 设置请求头必须在open之后,send之前 请求头里的设置好比…

mysql8 Found option without preceding group错误

这个错误说起来是真的坑&#xff0c;今晚帮同学在window操作系统上安装mysql8当自定义my.ini文件的时候 就出现一下错误&#xff0c;死活启动不起来 一直报错。当删掉这个my.ini文件的时候却能启动&#xff0c;刚开始以为是my.ini里的配置选项不对&#xff0c;一个一个筛查后依…

Mysql 备份与恢复

日志 MySQL 的日志默认保存位置为 /usr/local/mysql/data 常见的日志 错误日志&#xff1a;mysql本身启动&#xff0c;停止&#xff0c;运行期间发生的错误信息一般查询日志二进制日志&#xff1a;用于基于日志形式的数据恢复。用于主从复制&#xff0c;实现主从同步&#xf…

赛宁网安有力保障淮安市网络安全技能竞赛决赛

9月6日&#xff0c;由中共淮安市委网信办、淮安市总工会、淮安市人社局、淮安市教育局、淮安市公安局、共青团淮安市委共同主办&#xff0c;淮阴工学院协办&#xff0c;淮安市网络信息和数据安全协会、淮安市信息安全等级保护工作协调小组办公室承办&#xff0c;中国电信股份有…

2023年MySQL实战核心技术第一篇

目录 四 . 基础架构&#xff1a;一条SQl查询语句是如何执行的&#xff1f; 4.1 MySQL逻辑架构图&#xff1a; 4.2 MySQL的Server层和存储引擎层 4.2.1 连接器 4.2.1.1 解释 4.2.1.2 MySQL 异常重启 解决方案&#xff1a; 4.2.1.2.1. 定期断开长连接&#xff1a; 4.2.1.2.2. 初始…