本文是学习Siki学院Plane老师的《定时回调系统技术专题》视频课程的学习笔记和总结
using System;
using System.Collections.Generic;
using System.Timers;
public class PETimer {
private Action taskLog;
private Action, int> taskHandle;
private static readonly string lockTid = "lockTid";
private DateTime startDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0);
private double nowTime;
private Timer srvTimer;
private int tid;
private List tidLst = new List();
private List recTidLst = new List();
private static readonly string lockTime = "lockTime";
private List tmpTimeLst = new List();
private List taskTimeLst = new List();
private List tmpDelTimeLst = new List();
private int frameCounter;
private static readonly string lockFrame = "lockFrame";
private List tmpFrameLst = new List();
private List taskFrameLst = new List();
private List tmpDelFrameLst = new List();
public PETimer(int interval = 0) {
tidLst.Clear();
recTidLst.Clear();
tmpTimeLst.Clear();
taskTimeLst.Clear();
tmpFrameLst.Clear();
taskFrameLst.Clear();
if (interval != 0) {
srvTimer = new Timer(interval) {
AutoReset = true
};
srvTimer.Elapsed += (object sender, ElapsedEventArgs args) => {
Update();
};
srvTimer.Start();
}
}
public void Update() {
CheckTimeTask();
CheckFrameTask();
DelTimeTask();
DelFrameTask();
if (recTidLst.Count > 0) {
lock (lockTid) {
RecycleTid();
}
}
}
private void DelTimeTask() {
if (tmpDelTimeLst.Count > 0) {
lock (lockTime) {
for (int i = 0; i < tmpDelTimeLst.Count; i++) {
bool isDel = false;
int delTid = tmpDelTimeLst[i];
for (int j = 0; j < taskTimeLst.Count; j++) {
PETimeTask task = taskTimeLst[j];
if (task.tid == delTid) {
isDel = true;
taskTimeLst.RemoveAt(j);
recTidLst.Add(delTid);
//LogInfo("Del taskTimeLst ID:" + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
break;
}
}
if (isDel)
continue;
for (int j = 0; j < tmpTimeLst.Count; j++) {
PETimeTask task = tmpTimeLst[j];
if (task.tid == delTid) {
tmpTimeLst.RemoveAt(j);
recTidLst.Add(delTid);
//LogInfo("Del tmpTimeLst ID:" + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
break;
}
}
}
}
}
}
private void DelFrameTask() {
if (tmpDelFrameLst.Count > 0) {
lock (lockFrame) {
for (int i = 0; i < tmpDelFrameLst.Count; i++) {
bool isDel = false;
int delTid = tmpDelFrameLst[i];
for (int j = 0; j < taskFrameLst.Count; j++) {
PEFrameTask task = taskFrameLst[j];
if (task.tid == delTid) {
isDel = true;
taskFrameLst.RemoveAt(j);
recTidLst.Add(delTid);
break;
}
}
if (isDel)
continue;
for (int j = 0; j < tmpFrameLst.Count; j++) {
PEFrameTask task = tmpFrameLst[j];
if (task.tid == delTid) {
tmpFrameLst.RemoveAt(j);
recTidLst.Add(delTid);
break;
}
}
}
}
}
}
private void CheckTimeTask() {
if (tmpTimeLst.Count > 0) {
lock (lockTime) {
//加入缓存区中的定时任务
for (int tmpIndex = 0; tmpIndex < tmpTimeLst.Count; tmpIndex++) {
taskTimeLst.Add(tmpTimeLst[tmpIndex]);
}
tmpTimeLst.Clear();
}
}
//遍历检测任务是否达到条件
nowTime = GetUTCMilliseconds();
for (int index = 0; index < taskTimeLst.Count; index++) {
PETimeTask task = taskTimeLst[index];
if (nowTime.CompareTo(task.destTime) < 0) {
continue;
}
else {
Action cb = task.callback;
try {
if (taskHandle != null) {
taskHandle(cb, task.tid);
}
else {
if (cb != null) {
cb(task.tid);
}
}
}
catch (Exception e) {
LogInfo(e.ToString());
}
//移除已经完成的任务
if (task.count == 1) {
taskTimeLst.RemoveAt(index);
index--;
recTidLst.Add(task.tid);
}
else {
if (task.count != 0) {
task.count -= 1;
}
task.destTime += task.delay;
}
}
}
}
private void CheckFrameTask() {
if (tmpFrameLst.Count > 0) {
lock (lockFrame) {
//加入缓存区中的定时任务
for (int tmpIndex = 0; tmpIndex < tmpFrameLst.Count; tmpIndex++) {
taskFrameLst.Add(tmpFrameLst[tmpIndex]);
}
tmpFrameLst.Clear();
}
}
frameCounter += 1;
//遍历检测任务是否达到条件
for (int index = 0; index < taskFrameLst.Count; index++) {
PEFrameTask task = taskFrameLst[index];
if (frameCounter < task.destFrame) {
continue;
}
else {
Action cb = task.callback;
try {
if (taskHandle != null) {
taskHandle(cb, task.tid);
}
else {
if (cb != null) {
cb(task.tid);
}
}
}
catch (Exception e) {
LogInfo(e.ToString());
}
//移除已经完成的任务
if (task.count == 1) {
taskFrameLst.RemoveAt(index);
index--;
recTidLst.Add(task.tid);
}
else {
if (task.count != 0) {
task.count -= 1;
}
task.destFrame += task.delay;
}
}
}
}
#region TimeTask
public int AddTimeTask(Action callback, double delay, PETimeUnit timeUnit = PETimeUnit.Millisecond, int count = 1) {
if (timeUnit != PETimeUnit.Millisecond) {
switch (timeUnit) {
case PETimeUnit.Second:
delay = delay * 1000;
break;
case PETimeUnit.Minute:
delay = delay * 1000 * 60;
break;
case PETimeUnit.Hour:
delay = delay * 1000 * 60 * 60;
break;
case PETimeUnit.Day:
delay = delay * 1000 * 60 * 60 * 24;
break;
default:
LogInfo("Add Task TimeUnit Type Error...");
break;
}
}
int tid = GetTid(); ;
nowTime = GetUTCMilliseconds();
lock (lockTime) {
tmpTimeLst.Add(new PETimeTask(tid, callback, nowTime + delay, delay, count));
}
return tid;
}
public void DeleteTimeTask(int tid) {
lock (lockTime) {
tmpDelTimeLst.Add(tid);
//LogInfo("TmpDel ID:" + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
}
/*
bool exist = false;
for (int i = 0; i < taskTimeLst.Count; i++) {
PETimeTask task = taskTimeLst[i];
if (task.tid == tid) {
//taskTimeLst.RemoveAt(i);
for (int j = 0; j < tidLst.Count; j++) {
if (tidLst[j] == tid) {
//tidLst.RemoveAt(j);
break;
}
}
exist = true;
break;
}
}
if (!exist) {
for (int i = 0; i < tmpTimeLst.Count; i++) {
PETimeTask task = tmpTimeLst[i];
if (task.tid == tid) {
//tmpTimeLst.RemoveAt(i);
for (int j = 0; j < tidLst.Count; j++) {
if (tidLst[j] == tid) {
//tidLst.RemoveAt(j);
break;
}
}
exist = true;
break;
}
}
}
return exist;
*/
}
public bool ReplaceTimeTask(int tid, Action callback, float delay, PETimeUnit timeUnit = PETimeUnit.Millisecond, int count = 1) {
if (timeUnit != PETimeUnit.Millisecond) {
switch (timeUnit) {
case PETimeUnit.Second:
delay = delay * 1000;
break;
case PETimeUnit.Minute:
delay = delay * 1000 * 60;
break;
case PETimeUnit.Hour:
delay = delay * 1000 * 60 * 60;
break;
case PETimeUnit.Day:
delay = delay * 1000 * 60 * 60 * 24;
break;
default:
LogInfo("Replace Task TimeUnit Type Error...");
break;
}
}
nowTime = GetUTCMilliseconds();
PETimeTask newTask = new PETimeTask(tid, callback, nowTime + delay, delay, count);
bool isRep = false;
for (int i = 0; i < taskTimeLst.Count; i++) {
if (taskTimeLst[i].tid == tid) {
taskTimeLst[i] = newTask;
isRep = true;
break;
}
}
if (!isRep) {
for (int i = 0; i < tmpTimeLst.Count; i++) {
if (tmpTimeLst[i].tid == tid) {
tmpTimeLst[i] = newTask;
isRep = true;
break;
}
}
}
return isRep;
}
#endregion
#region FrameTask
public int AddFrameTask(Action callback, int delay, int count = 1) {
int tid = GetTid();
lock (lockTime) {
tmpFrameLst.Add(new PEFrameTask(tid, callback, frameCounter + delay, delay, count));
}
return tid;
}
public void DeleteFrameTask(int tid) {
lock (lockFrame) {
tmpDelFrameLst.Add(tid);
}
/*
bool exist = false;
for (int i = 0; i < taskFrameLst.Count; i++) {
PEFrameTask task = taskFrameLst[i];
if (task.tid == tid) {
//taskFrameLst.RemoveAt(i);
for (int j = 0; j < tidLst.Count; j++) {
if (tidLst[j] == tid) {
//tidLst.RemoveAt(j);
break;
}
}
exist = true;
break;
}
}
if (!exist) {
for (int i = 0; i < tmpFrameLst.Count; i++) {
PEFrameTask task = tmpFrameLst[i];
if (task.tid == tid) {
//tmpFrameLst.RemoveAt(i);
for (int j = 0; j < tidLst.Count; j++) {
if (tidLst[j] == tid) {
//tidLst.RemoveAt(j);
break;
}
}
exist = true;
break;
}
}
}
return exist;
*/
}
public bool ReplaceFrameTask(int tid, Action callback, int delay, int count = 1) {
PEFrameTask newTask = new PEFrameTask(tid, callback, frameCounter + delay, delay, count);
bool isRep = false;
for (int i = 0; i < taskFrameLst.Count; i++) {
if (taskFrameLst[i].tid == tid) {
taskFrameLst[i] = newTask;
isRep = true;
break;
}
}
if (!isRep) {
for (int i = 0; i < tmpFrameLst.Count; i++) {
if (tmpFrameLst[i].tid == tid) {
tmpFrameLst[i] = newTask;
isRep = true;
break;
}
}
}
return isRep;
}
#endregion
public void SetLog(Action log) {
taskLog = log;
}
public void SetHandle(Action, int> handle) {
taskHandle = handle;
}
public void Reset() {
tid = 0;
tidLst.Clear();
recTidLst.Clear();
tmpTimeLst.Clear();
taskTimeLst.Clear();
tmpFrameLst.Clear();
taskFrameLst.Clear();
taskLog = null;
srvTimer.Stop();
}
public int GetYear() {
return GetLocalDateTime().Year;
}
public int GetMonth() {
return GetLocalDateTime().Month;
}
public int GetDay() {
return GetLocalDateTime().Day;
}
public int GetWeek() {
return (int)GetLocalDateTime().DayOfWeek;
}
public DateTime GetLocalDateTime() {
DateTime dt = TimeZone.CurrentTimeZone.ToLocalTime(startDateTime.AddMilliseconds(nowTime));
return dt;
}
public double GetMillisecondsTime() {
return nowTime;
}
public string GetLocalTimeStr() {
DateTime dt = GetLocalDateTime();
string str = GetTimeStr(dt.Hour) + ":" + GetTimeStr(dt.Minute) + ":" + GetTimeStr(dt.Second);
return str;
}
#region Tool Methonds
private int GetTid() {
lock (lockTid) {
tid += 1;
//安全代码,以防万一
while (true) {
if (tid == int.MaxValue) {
tid = 0;
}
bool used = false;
for (int i = 0; i < tidLst.Count; i++) {
if (tid == tidLst[i]) {
used = true;
break;
}
}
if (!used) {
tidLst.Add(tid);
break;
}
else {
tid += 1;
}
}
}
return tid;
}
private void RecycleTid() {
for (int i = 0; i < recTidLst.Count; i++) {
int tid = recTidLst[i];
for (int j = 0; j < tidLst.Count; j++) {
if (tidLst[j] == tid) {
tidLst.RemoveAt(j);
break;
}
}
}
recTidLst.Clear();
}
private void LogInfo(string info) {
if (taskLog != null) {
taskLog(info);
}
}
private double GetUTCMilliseconds() {
TimeSpan ts = DateTime.UtcNow - startDateTime;
return ts.TotalMilliseconds;
}
private string GetTimeStr(int time) {
if (time < 10) {
return "0" + time;
}
else {
return time.ToString();
}
}
#endregion
class PETimeTask {
public int tid;
public Action callback;
public double destTime;//单位:毫秒
public double delay;
public int count;
public PETimeTask(int tid, Action callback, double destTime, double delay, int count) {
this.tid = tid;
this.callback = callback;
this.destTime = destTime;
this.delay = delay;
this.count = count;
}
}
class PEFrameTask {
public int tid;
public Action callback;
public int destFrame;
public int delay;
public int count;
public PEFrameTask(int tid, Action callback, int destFrame, int delay, int count) {
this.tid = tid;
this.callback = callback;
this.destFrame = destFrame;
this.delay = delay;
this.count = count;
}
}
}
public enum PETimeUnit {
Millisecond,
Second,
Minute,
Hour,
Day
}
PETimer控制台工程案例代码:
using System;
using System.Threading;
using System.Collections.Generic;
namespace ConsoleProjects {
class Program {
private static readonly string obj = "lock";
static void Main(string[] args) {
Console.WriteLine("Test Start!");
//Test1();
Test2();
}
//第一种用法:运行线程检测并处理任务
static void Test1() {
//运行线程驱动计时
PETimer pt = new PETimer();
pt.SetLog((string info) => {
Console.WriteLine("LogInfo:" + info);
});
pt.AddTimeTask((int tid) => {
Console.WriteLine("Process线程ID:{0}", Thread.CurrentThread.ManagedThreadId.ToString());
}, 10, PETimeUnit.Millisecond, 0);
while (true) {
pt.Update();
}
}
//第二种用法:独立线程检测并处理任务
static void Test2() {
Queue tpQue = new Queue();
//独立线程驱动计时
PETimer pt = new PETimer(5);
pt.SetLog((string info) => {
Console.WriteLine("LogInfo:" + info);
});
int id = pt.AddTimeTask((int tid) => {
Console.WriteLine("Process线程ID:{0}", Thread.CurrentThread.ManagedThreadId.ToString());
}, 3000, PETimeUnit.Millisecond, 0);
//设置回调处理器
/*
pt.SetHandle((Action cb, int tid) => {
if (cb != null) {
lock (obj) {
tpQue.Enqueue(new TaskPack(tid, cb));
}
}
});
*/
while (true) {
string ipt = Console.ReadLine();
if (ipt == "a") {
pt.DeleteTimeTask(id);
}
if (tpQue.Count > 0) {
TaskPack tp = null;
lock (obj) {
tp = tpQue.Dequeue();
}
tp.cb(tp.tid);
}
}
}
}
//任务数据包
class TaskPack {
public int tid;
public Action cb;
public TaskPack(int tid, Action cb) {
this.tid = tid;
this.cb = cb;
}
}
}
PETimer集成到Unity案例代码:
using UnityEngine;
public class GameStart : MonoBehaviour {
PETimer pt = new PETimer();
int tempID = -1;
private void Start() {
//时间定时
pt.AddTimeTask(TimerTask, 500, PETimeUnit.Millisecond, 3);
//帧数定时
pt.AddFrameTask(FrameTask, 100, 3);
//定时替换/删除
tempID = pt.AddTimeTask((int tid) => {
Debug.Log("定时等待替换......");
}, 1, PETimeUnit.Second, 0);
}
private void Update() {
pt.Update();
//定时替换
if (Input.GetKeyDown(KeyCode.R)) {
bool succ = pt.ReplaceTimeTask(tempID, (int tid) => {
Debug.Log("定时等待删除......");
}, 2, PETimeUnit.Second, 0);
if (succ) {
Debug.Log("替换成功");
}
}
//定时删除
if (Input.GetKeyDown(KeyCode.D)) {
pt.DeleteTimeTask(tempID);
}
}
void TimerTask(int tid) {
Debug.Log("TimeTask:" + System.DateTime.UtcNow);
}
void FrameTask(int tid) {
Debug.Log("FrameTask:" + System.DateTime.UtcNow);
}
}