多种Timer的场景应用
前言
今天讲讲各种Timer的使用。
三种Timer组件
.Net框架提供了三种常规Timer组件,分别是System.Windows.Forms.Timer、System.Timers.Timer和System.Threading.Timer。实际最常用的也就是前两种,而且应用场景比较明确。
System.Windows.Forms.Timer
Forms.Timer基于单线程,也就是基于主线程运行。它的应用场景一般是:
- Winform项目,WPF项目
- 小型任务操作,比如界面更新。而比如数据库交互这种需要等待和响应的操作就不合适了
- 不适合时间粒度比较细的项目。比如你要发起一个定时器,和硬件设备进行交互。此Timer是在主线程上进行中断,会造成硬件的交互和UI操作争夺资源。
Timer timerWft = new Timer(); //System.Windows.Forms.Timer
private void Form1_Load(object sender, EventArgs e)
{
//System.Windows.Forms.Timer
timerWft.Tick += TimerWft_Tick; //绑定触发事件
timerWft.Interval = 50; //设定执行间隔时间(ms)
}
/*
* System.Windows.Forms.Timer特点
* 1. 运行在主线程内,因此不是多线程运行
* 2. 因为运行在主线程内,所以它的执行机制是中断机制,即在UI消息泵内制造一个中断,来处理Timer的定时处理代码
* 3. 因为是中断机制,所以定时处理代码不能过于耗时,耗时将导致UI不流畅或者卡死
*
* 因此,这种Timer只能用于Winform下,定时处理代码很简单,耗时较小的应用场景
* 何为消息泵:Main方法中的打开主窗体的代码:Application.Run(new Form1())就是启动一个消息处理循环并打开主窗体
*/
private void btnWft_Click(object sender, EventArgs e)
{
if (!timerWft.Enabled)
{
lbMsg.Items.Clear();
btnWft.Text = "停止";
timerWft.Start(); //使用Start()方法或者Enable = true属性来启动Timer
//timerWft.Enabled = true;
}
else
{
btnWft.Text = "启动";
timerWft.Stop();
}
}
private void TimerWft_Tick(object sender, EventArgs e)
{
AddMsg(string.Format("{0}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")));
}
因为这个Timer是继承自Component的,因此它可以从工具箱上拖放到设计界面上。不过一般不建议这么做,因为手写也非常方便。
System.Timers.Timer
这个Timer基于多线程的:
- 不限项目类型
- 其他不适合Windows.Forms.Timer的场景,都建议使用它
NsTim.Timer timerTim = new NsTim.Timer(); //System.Timers.Timer
private void Form1_Load(object sender, EventArgs e)
{
//System.Timers.Timer
timerTim.Elapsed += TimerTim_Elapsed; //绑定触发事件
timerTim.Interval = 50; //设定执行间隔时间(ms)
}
/* System.Timers.Timer 的特点
* 1. Timer代码在独立线程内运行,原则上不会卡UI界面
* 2. 因为是多线程执行,所以定时执行的方法是由子线程触发的,而它要操控UI线程的对象,此时需要跨线程
*/
private void btnTim_Click(object sender, EventArgs e)
{
if (!timerTim.Enabled)
{
lbMsg.Items.Clear();
btnTim.Text = "停止";
timerTim.Start(); //使用Start()方法或者Enable = true属性来启动Timer
//timerWft.Enabled = true;
}
else
{
btnTim.Text = "启动";
timerTim.Stop();
}
}
private void TimerTim_Elapsed(object sender, NsTim.ElapsedEventArgs e)
{
if (this.IsHandleCreated) //判断IsHandleCreated,以避免窗体关闭时窗体句柄销毁导致 this 失效,而导致后续执行异常
{
//必须使用BeginInvoke异步调用UI对象
this.BeginInvoke(new Action(() =>
{
AddMsg(string.Format("{0}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")));
}));
}
}
我们可以看到,在Timer的线程中和UI交互,必须使用BeginInvoke()发起异步跨线程操作,或者使用Invoke()发起同步跨线程操作。为了不卡界面,一般都会使用BeginInvoke异步调用。
System.Threading.Timer
这个Timer也是基于多线程的,但我们仔细分析它的命名空间,我们发现它是有侧重的。上面第二种是System.Timers,侧重于Timers,可以说它是“专业的Timer组件”,而这个Timer的命名空间是System.Threading,是基于线程的Timer,可以理解为,它是为线程中的场景服务的。它的特点:
- 不限项目类型
- 适合基于多线程中的Timer场景,比如想在多线程执行中发起一个Timer
所以我们会发现,如果想按常规的方式使用这个Timer,是很别扭的。常规的Timer,都可以自由控制Start(), Stop(), 但它没有。所以它适合的是“多线程中的Timer场景”,可以说是非常精确的一个描述。理由如下:
- 命名空间的定义Threading,说明了设计者想要它用于多线程场景
- 它没有定义显式的停止Timer的方法,因为在线程运行场景下没有必要。一个线程的正确用法,往往是应该迅速执行、迅速结束的,因此没有必要给它提供Stop方法。所以我们发现,停止这个Timer的方式,是 timer = null,将Timer扔给垃圾收集器。
- 从它的方法参数中,可以看到它的回调方法和其他的Timer不同,其他的Timer都是event,而它是Callbacks,适合用匿名委托,把代码写在线程方法内,这样线程代码和回调代码不分离,可读性更好。
NsThr.Timer timerThr; //System.Threading.Timer
/* System.Threading.Timer的特点
* 1. Timer代码在独立线程内运行,原则上不会卡UI界面
* 2. 因为是多线程执行,所以定时执行的方法是由子线程触发的,而它要操控UI线程的对象,此时需要跨线程
* 3. 可以看出它的停止代码写的非常丑陋,主要是它并没有提供停止Timer的方法。它的使用场景是在多线程中发起Timer,一个线程的执行原则上很快结束,所以没有必要
* 4. 我们会发现这个Timer继承自MarshalByRefObject,这是个具有跨应用程序域特性的对象,比如应用程序域A的代码要操控应用程序域B的Timer,那只能用它了。
*/
private void btnThr_Click(object sender, EventArgs e)
{
if (timerThr == null)
{
lbMsg.Items.Clear();
btnThr.Text = "停止";
timerThr = new NsThr.Timer(new NsThr.TimerCallback((o)=>
{
if (this.IsHandleCreated)
{
this.BeginInvoke(new Action(() =>
{
AddMsg(string.Format("{0}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")));
}));
}
}), null, 50, 50);
}
else
{
btnThr.Text = "启动";
timerThr.Change(0, NsThr.Timeout.Infinite);
timerThr = null;
}
}
总结
System.Windows.Forms.Timers 适合基于Winform,WPF的项目,同时执行任务比较简单快速的场景,比如UI显示。
System.Timers.Timer 适合各种项目,用于执行任务耗时较长,以及对交互有更高要求的场景。
System.Threading.Timer 适合用于在多线程中发起Timer的场景。
本文附有demo源代码:

需要的可通过以下方式下载:
- 扫描以下二维码关注公众号
- 在后台回复“Timers”,得到下载链接
觉得文章有意义的话,请动动手指,分享给朋友一起来共同学习进步。
欢迎关注微信公众号 “产品技术知与行” ,打造全面的结构化知识库,包括原创文章、免费课程(C#,Java,Js)、技术专题、视野知识、源码下载等内容。

扫描二维码关注
多种Timer的场景应用的更多相关文章
- GIT入门笔记(10)- 多种撤销修改场景和对策
场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- file. 场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步, ...
- GIT入门笔记(11)- 多种撤销修改场景和对策--实战练习
1.检查发现目前没有变化$ git statusOn branch masternothing to commit, working tree clean $ cat lsq.txt2222 2.修改 ...
- Swoole Timer 的应用
目录 你好,Swoole Timer 应用场景 参考文档 你好,Swoole PHP 的协程高性能网络通信引擎,使用 C/C++ 语言编写,提供了多种通信协议的网络服务器和客户端模块. Swoole ...
- MMORPG大型游戏设计与开发(服务器 游戏场景 核心详述)
核心这个词来的是多么的高深,可能我们也因为这个字眼望而却步,也就很难去掌握这部分的知识.之所以将核心放在最前面讲解,也可以看出它真的很重要,希望朋友们不会错过这个一直以来让大家不熟悉的知识,同我一起进 ...
- Android 进阶15:HandlerThread 使用场景及源码解析
眼睛困得要死,但今天的计划不完成又怎么能睡呢?明日复明日,明日何其多啊! 读完本文你将了解: HandlerThread 简介 HandlerThread 源码 HandlerThread 的使用场景 ...
- Storm介绍(一)
作者:Jack47 PS:如果喜欢我写的文章,欢迎关注我的微信公众账号程序员杰克,两边的文章会同步,也可以添加我的RSS订阅源. 内容简介 本文是Storm系列之一,介绍了Storm的起源,Storm ...
- 京东MySQL监控之Zabbix优化、自动化
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://wangwei007.blog.51cto.com/68019/1833332 随 ...
- 转:用WCAT进行IIS压力测试
Microsoft的Web容量分析工具(WCAT) 是测试你的客户-服务器网络配置的必备工具.这个工具在你的网络上对多种工作量的场景进行仿真,允许你确定你的网络和服务器的最佳配置.WCAT是专门为 评 ...
- 在SharePoint中无代码开发InfoPath应用: 获取当前用户信息
很多种不同的场景下,会需要得到当前的用户信息,例如需要根据当前用户判断组,进而控制权限. 首先InfoPath提供了一个userName方法,来实现这个目的,不过这个方法的问题是只能获得不包含域名的用 ...
随机推荐
- 基于vue-cli配置移动端自适应
移动端自适应:手淘的 lib-flexible + rem 配置 flexible 安装 lib-flexible 在命令行中运行如下安装: 1 npm i lib-flexible --save 引 ...
- [LeetCode] Maximize Distance to Closest Person 离最近的人的最大距离
In a row of seats, 1 represents a person sitting in that seat, and 0 represents that the seat is emp ...
- Document APIs
本节首先简要介绍Elasticsearch的数据复制模型,然后详细描述以下CRUD API: Single document APIs Index API Get API Delete API Upd ...
- HTML块元素,行内元素,类,头部元素
总结HTML块元素,行内元素,类,头部元素 块元素: 在HTML中,块级元素的高度为其内容的高度,宽度会扩展到与父容器同宽.默认情况下,块级元素会独占一行,并且元素前后行留空. 示例:<h1&g ...
- 初次接触之R语言
一.什么是R? 最受欢迎的数据分析和可视化平台之一. 其他分析平台:Excel.SPSS.SAS 二.为什么选择R? 免费.支持WINDOWS/MAC OS/Linux. 开源
- 关于分布式环境下的id生成
public class IdWorker { //基准时间 public const long Twepoch = 1288834974657L; //机器标识位数 ; //数据标志位数 ; //序 ...
- JavaMail技术实现邮件发送转【】
1.导入2个jar包,mail.jar,activation.jar 2.导入的jar包与myeclipse中自带的javaee 中的javaee.jar中的javax.activation包及jav ...
- 接口调试之Postman 使用方法详解
一.Postman背景介绍 用户在开发或者调试网络程序或者是网页B/S模式的程序的时候是需要一些方法来跟踪网页请求的,用户可以使用一些网络的监视工具比如著名的Firebug等网页调试工具.今天给大家介 ...
- 吴恩达机器学习笔记55-异常检测算法的特征选择(Choosing What Features to Use of Anomaly Detection)
对于异常检测算法,使用特征是至关重要的,下面谈谈如何选择特征: 异常检测假设特征符合高斯分布,如果数据的分布不是高斯分布,异常检测算法也能够工作,但是最好还是将数据转换成高斯分布,例如使用对数函数:
- [Swift]LeetCode541. 反转字符串 II | Reverse String II
Given a string and an integer k, you need to reverse the first k characters for every 2k characters ...