在 Winform 等平台开发中,经常会用到定时器的功能,但项目定时器一旦写多了,容易使软件变卡,而且运行时间长了会造成软件的闪退,这个可能是内存溢出造成的,具体原因我也没去深究,另一个,就是在关闭软件时,经常关不掉,因为这时候定时器的线程依然还在运行,你就要把这些定时器一个个关闭,才能关闭软件,或者直接使用强制退出程序代码,非常的麻烦,后面我在想能不能封装一个定时器,使用事件的订阅机制来实现功能,后面就实现了这些功能,那么下面就开始搬代码吧。
新建一个 winform 项目,添加一个类 ScanTimer.cs 。
代码:
using System;
using System.Threading;
///
/// 定时器
///
public class ScanTimer
{
///
/// 定时器回调事件
///
public static event Action ScanEvent;
///
/// 定时器开关的状态
///
public static Action TimerStatus = null;
///
/// 定时器执行的次数
///
public static Action TimerExecuteCount = null;
///
/// 定时器是否打开
///
public static bool IsOpen
{
get
{
if (Timer == null)
return false;
return Timer.Enabled;
}
}
//定时器
private static System.Timers.Timer Timer = null;
//间隔时间
private const int IntervalTime = 2000;
//定时清理控制台日志
private static int OutCount = 0;
//是否初始化
private static bool IsInit = false;
///
/// 初始化
///
private static void Init()
{
//实例化Timer类,
Timer = new System.Timers.Timer();
//设置间隔时间(毫秒);
Timer.Interval = IntervalTime;
//到达时间的时候执行事件;
Timer.Elapsed += new System.Timers.ElapsedEventHandler(Elapsed);
//设置是执行一次(false)还是一直执行(true);
Timer.AutoReset = true;
IsInit = true;
}
///
/// 定时器
///
///
///
private static void Elapsed(object source, System.Timers.ElapsedEventArgs e)
{
OutCount++;
if (OutCount > 10000)
{
OutCount = 0;
//Console.Clear();
}
//定时器的执行次数
if (TimerExecuteCount != null)
TimerExecuteCount(OutCount);
//执行回调
if (ScanEvent != null)
ScanEvent();
}
///
/// 打开定时器
///
public static void Start()
{
if (!IsInit) Init();
Timer.Enabled = true;
OutCount = 0;
if (TimerStatus != null)
TimerStatus(true);
}
///
/// 关闭定时器
///
public static void Stop()
{
Timer.Enabled = false;
OutCount = 0;
if (TimerStatus != null)
TimerStatus(false);
}
///
/// 清除所有的事件
///
public static void ClearAllEvent()
{
if (ScanEvent == null) return;
Delegate[] dels = ScanEvent.GetInvocationList();
foreach (Delegate del in dels)
{
ScanEvent -= del as Action;
}
Console.WriteLine("[ClearAllEvent]清除定时器所有的事件");
}
///
/// 获取定时器任务的个数
///
///
public static int GetTaskCount()
{
if (ScanEvent == null) return 0;
Delegate[] dels = ScanEvent.GetInvocationList();
return dels.Length;
}
///
/// 是否存在某个任务
///
///
///
public static bool IsExistTask(string taskName)
{
if (ScanEvent == null)
return false;
Delegate[] dels = ScanEvent.GetInvocationList();
foreach (Delegate del in dels)
{
object delObj = del.GetType().GetProperty("Method").GetValue(del, null);
string funcName = (string)delObj.GetType().GetProperty("Name").GetValue(delObj, null);
if (funcName == taskName) return true;
}
return false;
}
private ScanTimer() { }
}
给 Form1 窗体添加 Form1_Load,Form1_FormClosing 事件。
Form1_Load 事件是窗体启动时执行一次,在这里可以启动定时器。
Form1_FormClosing 事件是在窗体关闭时执行一次,在这里我们可以关闭定时器。
如下图:
ScanTimer 的 ScanEvent 字段是一个事件,这里就是我们需要添加订阅机制的地方。
public static event Action ScanEvent;
其实原理也很简单,就是在 ScanTimer 类中封装一个定时器,每隔多少秒去执行一次这个事件,然后所有订阅这个事件的方法,都会被执行到。
在调用 ScanTimer 类之前,先把 winform 的输出类型改为 控制台应用程序,这样更方便的观察。
代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
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)
{
ScanTimer.ScanEvent += Timer;
ScanTimer.Start();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
ScanTimer.Stop();
}
private void Timer()
{
Console.WriteLine("定时器");
}
}
}
调用也非常的简单,只需要写一个 Timer() 方法,然后添加到 ScanEvent 这个事件中就好了,我们运行看看效果
另外,在 ScanTimer 类中我还封装了其他的方法,比如,你不知道定时器是否还在执行中,有时候执行代码,不一定需要在代码中打印, 可以使用 TimerExecuteCount 这个委托查看定时器执行了多少次,也可以通过 TimerStatus 这个委托,来查看定时器是否在执行。
代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
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)
{
ScanTimer.ScanEvent += Timer;
ScanTimer.TimerStatus += TimerStatus;
ScanTimer.TimerExecuteCount += TimerExecuteCount;
ScanTimer.Start();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
ScanTimer.Stop();
}
private void Timer()
{
Console.WriteLine("定时器");
}
private void TimerStatus(bool sw)
{
Console.WriteLine("定时器的状态:{0}", sw);
}
private void TimerExecuteCount(int count)
{
Console.WriteLine("定时器执行次数:{0}", count);
}
}
}
运行:
在这里,TimerExecuteCount 和 TimerStatus 这两个委托是 Action 类型,如果其他的类不需要订阅这两个委托,不用使用 += ,可以直接使用 = 号就行。
ScanTimer.ScanEvent += Timer;
ScanTimer.TimerStatus = TimerStatus;
ScanTimer.TimerExecuteCount = TimerExecuteCount;
如果这个帖子对你有所帮助,欢迎 关注 、点赞 、留言
end