一、概述
前不久写了一篇名为 “C# 定时器封装版” 的帖子,它是用的定时器 + 事件订阅 的方式完成的,虽然可以实现需求,但是它有个缺点,就是定时器的执行的间隔时间只能用固定的时间,假设你想每个事件有自己的单独间隔时间那是不行,于是后面我在想如何解决这个问题,让加入的每一个委托都能设置自己的间隔时间,劈里啪啦乱写一通后,终于实现了,虽然写的不是特别好,但还是可以用的。
最近写了很多篇关于定时器帖子,包括一篇 C# 模拟 Unity3d 协程的帖子,也是为了解决大型项目中的定时器混乱问题,那么定时器使用过多会造成那些危害呢?
定时器会造成:
1.关闭程序之前,假设不关闭定时器,有时候程序都关闭不了,一直处于卡死的状态。
2.程序运行的时间长了,很容易闪退。
3.定时器没有统一管理,比较混乱,时间久了,自己都不记得用了几个定时器了。
如果用一个定时器,解决所有的需求,不更符合设计模式的“单一职责原则” 。
二、实现功能
新建一个 winform 项目,给界面随便添加几个按钮,如下:
添加一个类 TimerInfo
using System;
internal class TimerInfo
{
/// <summary>
/// 定时器的名字(关闭定时器用)
/// </summary>
public string TimerName { get; set; }
/// <summary>
/// 定时器委托
/// </summary>
public Action Tick { get; set; }
/// <summary>
/// 间隔时间(毫秒)
/// </summary>
public int Interval { get; set; }
/// <summary>
/// 开始执行的时间
/// </summary>
public DateTime StartTimer { get; set; }
}
TimerInfo 的作用相当于一个任务的结构体,任务的名字,委托,和间隔时间需要赋值,StartTimer 则是当前任务倒计时的开始时间,这里不用赋值。
添加一个类 TimeInterval
using System;
/// <summary>
/// 时间差计算
/// </summary>
public class TimeInterval
{
/// <summary>
/// 计算两个时间间隔的时长
/// </summary>
/// <param name="TimeType">返回的时间类型</param>
/// <param name="StartTime">开始时间</param>
/// <param name="EndTime">结束时间</param>
/// <returns>返回间隔时间,间隔的时间类型根据参数 TimeType 区分</returns>
public static double GetSpanTime(TimeType TimeType, DateTime StartTime, DateTime EndTime)
{
TimeSpan ts1 = new TimeSpan(StartTime.Ticks);
TimeSpan ts2 = new TimeSpan(EndTime.Ticks);
TimeSpan ts = ts1.Subtract(ts2).Duration();
//TimeSpan ts = EndTime - StartTime;
double result = 0f;
switch (TimeType)
{
case TimeType.MilliSecond:
result = ts.TotalMilliseconds;
break;
case TimeType.Seconds:
result = ts.TotalSeconds;
break;
case TimeType.Minutes:
result = ts.TotalMinutes;
break;
case TimeType.Hours:
result = ts.TotalHours;
break;
case TimeType.Days:
result = ts.TotalDays;
break;
}
return result;
}
private TimeInterval() { }
}
/// <summary>
/// 时间类型
/// </summary>
public enum TimeType
{
/// <summary>
/// 毫秒
/// </summary>
MilliSecond,
/// <summary>
/// 秒
/// </summary>
Seconds,
/// <summary>
/// 分钟
/// </summary>
Minutes,
/// <summary>
/// 小时
/// </summary>
Hours,
/// <summary>
/// 天
/// </summary>
Days,
/// <summary>
/// 月
/// </summary>
Months
}
TimeInterval 类的主要作用是判断两个时间间隔了多久。
添加一个类 TimerOptimize
using System;
using System.Collections.Generic;
using System.Linq;
internal class TimerOptimize
{
private static List<TimerInfo> TimerList = new List<TimerInfo>();
private static List<string> RemoveList = new List<string>();
private static System.Timers.Timer Timer1 = null;
private static void Init()
{
Timer1 = new System.Timers.Timer();
Timer1.Interval = 200;
Timer1.AutoReset = true;
Timer1.Elapsed += Timer1_Elapsed;
}
/// <summary>
/// 添加定时器
/// </summary>
/// <param name="timer"></param>
public static void Add(TimerInfo timer)
{
if (!Check(timer)) return;
timer.StartTimer = DateTime.Now;
TimerList.Add(timer);
if (Timer1 != null && !Timer1.Enabled)
Timer1.Enabled = true;
}
/// <summary>
/// 移除定时器
/// </summary>
/// <param name="timerName"></param>
public static void Remove(string timerName)
{
if (string.IsNullOrEmpty(timerName)) return;
if (RemoveList.Contains(timerName)) return;
var collect = TimerList.Where(p => p.TimerName == timerName).ToList();
if (collect == null || collect.Count == 0) return;
RemoveList.Add(timerName);
}
private static void Timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (TimerList.Count > 0)
{
foreach (var item in TimerList)
{
double timer = TimeInterval.GetSpanTime(TimeType.MilliSecond, item.StartTimer, DateTime.Now);
if (timer > item.Interval)
{
item.StartTimer = DateTime.Now;
if (item.Tick != null)
item.Tick();
}
}
}
if (RemoveList.Count > 0)
{
List<int> removeIndexList = new List<int>();
for (int i = 0; i < RemoveList.Count; i++)
{
int index = TimerList.FindIndex(p => p.TimerName == RemoveList[i]);
if (index >= 0)
{
TimerList.RemoveAt(index);
removeIndexList.Add(i);
Console.WriteLine("[TimerOptimize]移除定时器 {0}", RemoveList[i]);
}
}
if (removeIndexList.Count > 0)
{
for (int i = 0; i < removeIndexList.Count; i++)
{
RemoveList.RemoveAt(removeIndexList[i]);
}
}
if (Timer1 != null && TimerList.Count == 0)
{
Timer1.Enabled = false;
Console.WriteLine("[TimerOptimize]定时器已经关闭");
}
}
}
private static bool Check(TimerInfo timer)
{
if (timer == null)
{
Console.WriteLine("[TimerOptimize]timer 不能为空");
return false;
}
if (timer.Tick == null)
{
Console.WriteLine("[TimerOptimize]timer.Tick 不能为空");
return false;
}
if (string.IsNullOrEmpty(timer.TimerName))
{
Console.WriteLine("[TimerOptimize]timer.TimerName 不能为空");
return false;
}
if(timer.Interval < 200)
{
Console.WriteLine("[TimerOptimize]timer.Interval 间隔时间太短");
return false;
}
if (TimerList.Any(p => p.TimerName == timer.TimerName))
{
Console.WriteLine("[TimerOptimize]不能重复的添加,TimerName:{0}", timer.TimerName);
return false;
}
return true;
}
static TimerOptimize()
{
Init();
}
private TimerOptimize()
{
}
~TimerOptimize()
{
Timer1.Enabled = false;
}
}
TimerOptimize 类的主要作用是存储这些任务,判断什么时候执行回调,什么时候移除任务等。
主要代码都在这里了,下面开始测试。
三、测试
Form1 代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace 定时器
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
TimerOptimize.Add(new TimerInfo()
{
TimerName = "Timer1",
Tick = Test1,
Interval = 1000
});
Console.WriteLine("添加 timer1");
}
private void button2_Click(object sender, EventArgs e)
{
TimerOptimize.Add(new TimerInfo()
{
TimerName = "Timer2",
Tick = Test2,
Interval = 2000
});
Console.WriteLine("添加 timer2");
}
private void button3_Click(object sender, EventArgs e)
{
TimerOptimize.Add(new TimerInfo()
{
TimerName = "Timer3",
Tick = Test3,
Interval = 3000
});
Console.WriteLine("添加 timer3");
}
private void button4_Click(object sender, EventArgs e)
{
TimerOptimize.Remove("Timer1");
}
private void button5_Click(object sender, EventArgs e)
{
TimerOptimize.Remove("Timer2");
}
private void button6_Click(object sender, EventArgs e)
{
TimerOptimize.Remove("Timer3");
}
private void Test1()
{
Console.WriteLine("定时器1");
}
private void Test2()
{
Console.WriteLine("定时器2");
}
private void Test3()
{
Console.WriteLine("定时器3");
}
}
}
执行定时器1,并取消任务
执行定时器2,3,并取消任务
如果 TimerOptimize 类的 TimerList 为空时,会自动关闭定时器,这样能有效的节约系统资源,我粗略的看了下倒计时的时间,差不多是准确的,那么功能也就实现了,如果有需要改进的地方,欢迎留言告诉我,谢谢!
结束
如果这个帖子对你有所帮助,欢迎 关注 、点赞 、留言
end