目录
1.System.Timers.Timer
2.System.Windows.Forms.Timer
3.System.Threading.Timer
4.简单的封装
这里介绍三种常用的定时器,方便查阅资料或直接复制使用。
1.System.Timers.Timer
System.Timers.Timer
类定义了一个计时器,该计时器按固定间隔触发事件。它主要用于多线程环境,特别是在基于服务器的组件或服务组件中。此类没有用户界面,在运行时不可见,特别适用于后台任务处理。
主要属性和方法
- Interval:以毫秒为单位,设置或获取引发
Elapsed
事件的时间间隔。 - AutoReset:设置或获取一个值,该值指示计时器在
Elapsed
事件触发后是否自动重置,并继续计时(true
)或停止计时(false
)。 - Enabled:设置或获取一个值,该值指示计时器是否已启用(
true
)或已禁用(false
)。 - Start():通过将
Enabled
设置为true
来启动计时器。 - Stop():通过将
Enabled
设置为false
来停止计时器。 - Elapsed 事件:当指定的时间间隔过去时,将触发此事件。该事件处理函数在新的线程中执行,因此不能直接访问UI控件(需通过委托和Invoke方法)。
使用场景
适用于需要按固定时间间隔执行任务的后台服务或应用程序,如定期检查数据库、更新状态信息等。
用法:
namespace Test2
{
internal class Program
{
private static System.Timers.Timer Timer;
static void Main(string[] args)
{
Timer = new System.Timers.Timer();
Timer.Interval = 2000; //间隔时间2000毫秒
Timer.AutoReset = true;//是否重复执行
Timer.Elapsed += Timer_Elapsed;
Timer.Enabled = true; //或者使用 Timer.Start() 效果一样
Console.ReadKey();
}
private static void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
Console.WriteLine("需要重复执行的代码");
}
}
}
2.System.Windows.Forms.Timer
System.Windows.Forms.Timer
控件是Windows窗体环境中的定期事件引发组件。它主要用于单线程环境,特别是在UI更新中。该计时器是同步的,意味着它在UI线程上执行,不会阻塞或干扰UI操作。
主要属性和方法
- Interval:以毫秒为单位,设置或获取计时器事件之间的时间间隔。
- Enabled:设置或获取一个值,该值指示计时器是否已启用(
true
)或已禁用(false
)。 - Start():启动计时器。
- Stop():停止计时器。
- Tick 事件:当指定的时间间隔过去时,将触发此事件。与
System.Timers.Timer
不同,Tick
事件在UI线程上执行,因此可以直接访问UI控件。
使用场景
适用于需要按固定时间间隔更新UI元素的场景,如时钟、动画、进度条更新等。
System.Windows.Forms.Timer 可以在 Winform 工具箱中直接拖入到 Winform 界面中,也可以直接在代码中添加,System.Windows.Forms.Timer 在定时器回调中可以调用 Winform 的界面控件,而 System.Timers.Timer 回调内使用 Winform 控件会存在跨线程的错误。
用法同 System.Timers.Timer 差不多,用工具箱中的 Timer 可以少写很多代码。
代码:
using System;
using System.Windows.Forms;
namespace Test1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
System.Windows.Forms.Timer Timer;
private void Form1_Load(object sender, EventArgs e)
{
Timer = new System.Windows.Forms.Timer();
Timer.Interval = 1000;
Timer.Tick += Timer_Tick;
Timer.Start();
}
private void Timer_Tick(object sender, EventArgs e)
{
Console.WriteLine("需要重复执行的代码");
}
}
}
3.System.Threading.Timer
System.Threading.Timer
类由线程池调用,专门用于在线程池线程上按固定间隔执行单个回调方法。它是为服务器或后台应用程序设计的,可以优化系统资源的使用。
主要属性和方法
- Callback:定义在指定时间间隔后调用的回调方法。
- State:传递给回调方法的对象,作为回调方法的参数。
- DueTime:计时器第一次触发之前延迟的时间(以毫秒为单位)。
- Period:计时器每次触发之间的时间间隔(以毫秒为单位)。设置为
Timeout.Infinite
表示计时器只触发一次。 - Change(Int64, Int64):在计时器启动后更改其
DueTime
和Period
设置。 - Dispose():释放
Timer
对象使用的资源。
使用场景
适用于需要定期执行长时间运行或资源密集型任务的后台应用程序,如文件清理、数据同步等。由于回调方法在线程池线程上执行,因此不会影响UI线程的性能。
代码:
namespace Test2
{
internal class Program
{
private static System.Threading.Timer Timer;
static void Main(string[] args)
{
//参数1 要定时执行的回调
//参数2 定时回调的参数
//参数3 延迟多少秒开始执行
//参数4 定时器的间隔时间
Timer = new System.Threading.Timer(OnTimedEvent, null, 0, 1000);
Console.ReadKey();
}
private static void OnTimedEvent(Object state)
{
Console.WriteLine("需要重复执行的代码");
}
}
}
可以使用下面的方法来停止定时器
// 暂时停止定时器
Timer.Change(Timeout.Infinite, Timeout.Infinite);
如果需要继续执行定时器,可以使用下面的方法
// 重新启动定时器:初始延迟1000毫秒,每2000毫秒触发一次
Timer.Change(1000, 2000);
其实还有一个定时器 System.Web.UI.Timer,只是现在用的人比较少,这里就不做介绍了。
4.简单的封装
根据上面的代码可以发现,就那几行代码,复制来复制去的有点麻烦,我这里稍微封装一下,一句代码搞定定时器,非常的方便。
新建一个类 TimerScheduler
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
/// <summary>
/// 定时器的封装
/// </summary>
public class TimerScheduler
{
private static Dictionary<string, ActionSchedulerInfo> SchedulerDic = new Dictionary<string, ActionSchedulerInfo>();
/// <summary>
/// 添加定时执行委托
/// </summary>
/// <param name="key">唯一的Key</param>
/// <param name="intervalInSeconds">定时器间隔时间</param>
/// <param name="isUIThread">是否是UI线程</param>
/// <param name="action">定时器回调</param>
public static void Add(string key, double intervalInSeconds, bool isUIThread, Action action)
{
if (action == null)
{
Console.WriteLine("参数 action 不能为空");
return;
}
if (string.IsNullOrEmpty(key))
{
Console.WriteLine("参数 key 不能为空");
return;
}
if (SchedulerDic.ContainsKey(key))
{
Console.WriteLine($"{key} 不能重复的添加");
return;
}
if (intervalInSeconds <= 0 || intervalInSeconds > 86400)
{
Console.WriteLine($"{key} 的间隔时间超过了指定的范围");
return;
}
var actionScheduler = new ActionSchedulerInfo();
if (isUIThread)
{
actionScheduler.timerUI = new System.Windows.Forms.Timer();
actionScheduler.timerUI.Interval = (int)TimeSpan.FromSeconds(intervalInSeconds).TotalMilliseconds;
actionScheduler.timerUI.Tick += (sender, e) => action();
actionScheduler.timerUI.Start();
}
else
{
actionScheduler.timer = new System.Timers.Timer();
actionScheduler.timer.Interval = TimeSpan.FromSeconds(intervalInSeconds).TotalMilliseconds;
actionScheduler.timer.Elapsed += (sender, e) => Task.Run(action);
actionScheduler.timer.AutoReset = true;
actionScheduler.timer.Start();
}
SchedulerDic[key] = actionScheduler;
}
/// <summary>
/// 移除定时执行委托
/// </summary>
/// <param name="key"></param>
public static void Remove(string key)
{
if (SchedulerDic.TryGetValue(key, out var scheduler))
{
if (scheduler.timer != null)
{
scheduler.timer.Stop();
scheduler.timer.Dispose();
}
if (scheduler.timerUI != null)
{
scheduler.timerUI.Stop();
scheduler.timerUI.Dispose();
}
scheduler.actions = null;
SchedulerDic.Remove(key);
}
}
/// <summary>
/// 移除所有的定时器
/// </summary>
public static void Dispose()
{
foreach (var scheduler in SchedulerDic.Values)
{
if (scheduler.timer != null)
{
scheduler.timer.Stop();
scheduler.timer.Dispose();
scheduler.actions = null;
}
if (scheduler.timerUI != null)
{
scheduler.timerUI.Stop();
scheduler.timerUI.Dispose();
scheduler.actions = null;
}
}
SchedulerDic.Clear();
}
public class ActionSchedulerInfo
{
public System.Timers.Timer timer { get; set; }
public System.Windows.Forms.Timer timerUI { get; set; }
public Action actions { get; set; }
}
}
添加:
TimerScheduler.Add("Test", 2, false, () =>
{
Console.WriteLine("这是一个定时器");
});
这里使用了一个 Lambda 表达式,你可以直接替换成一个无参数的方法。
移除:
TimerScheduler.Remove("Test");
移除对应的 key,就能停止和清除这个定时器了。
end