c# 区分几种定时器(timer)

 更新时间:2020年7月13日 21:27  点击:2353

1、前言

不知道你是否对.NET里面的定时器产生过一些疑问,以下是武小栈个人的一些总结。

2、官方介绍

在.NET的框架之内定时器有四种,先看一下微软官方对他们各自特点介绍:

  • System.Timers.Timer,它将触发事件,并定期在一个或多个事件接收器中执行代码。 类旨在用作多线程环境中基于服务器的组件或服务组件;它没有用户界面,在运行时不可见。
  • System.Threading.Timer,它按固定的时间间隔对线程池线程执行单个回调方法。 回调方法是在实例化计时器时定义的,无法更改。 与 System.Timers.Timer 类一样,此类用作多线程环境中基于服务器的或服务组件;它没有用户界面,在运行时不可见。
  • System.Windows.Forms.Timer (仅 .NET Framework),这是一个触发事件并定期在一个或多个事件接收器中执行代码的 Windows 窗体组件。 组件没有用户界面,旨在在单线程环境中使用;它在 UI 线程上执行。
  • System.Web.UI.Timer (仅 .NET Framework),是一种定期执行异步或同步网页回发的 ASP.NET 组件。

再看看微软对开发者的使用建议:

System.Threading.Timer 是一种简单的轻型计时器,它使用回调方法,并由线程池线程提供服务。 不建议与 Windows 窗体一起使用,因为它的回调不会在用户界面线程上发生。 System.Windows.Forms.Timer 是用于 Windows 窗体的更好选择。 对于基于服务器的计时器功能,您可以考虑使用 System.Timers.Timer,这会引发事件并具有其他功能。

3、个人体会

System.Threading.Timer Class

是一个基础类,使用起来不是太好用,各种用法较为原始,用的较少。

System.Windows.Forms.Timer Class

第一次接触的就是它,毕竟直接winform拖下来就行了,用的还是比较多,我通常用在运行一些刷新界面的代码,这些代码通常不会有什么逻辑运算,比如界面上需要显示一个倒计时。

在这个类使用中我遇到过两个疑惑,作为分享:

Q1:Tick实践会创建新线程执行吗?

A1:不会创建新的线程,始终在主线程里面运行Tick事件;

Q2:定时器会start()瞬间触发一次,还是等待Interval间隔后再触发?

A2:等待Interval间隔后再触发。

Q3:定时器start()和stop()时候Interval会累积吗?

A3:不累积,每次start()重新计时。

Q4:如果Tick事件内的代码未执行完成,但是下一次Tick定时已经达到会发生什么?

A4:不会强行终止未完成的代码,也不会因为上一次Tick事件代码未执行完成而不再触发,而是类似于栈的形式将之前未执行完成的代码堆积,后触发的Tick事件内的代码先执行,先触发未完成的代码后执行,具体可以看下面示例。

 public Form1()
 {
  InitializeComponent();
  timerForm.Tick += TimerForm_Tick;
 }

 private int num = 1;//一个序号,表示当前第几次进入Tick事件

 private int rowNum = 1;//一个全局的行号,记录一下总共AppendText多少次

 private void TimerForm_Tick(object sender, EventArgs e)
 {
  
  string s = $"我是第{num++}次";
  for (int i = 0; i < 5; i++)
  {
   textBox1.AppendText($"{rowNum++} {s} 序号i={i} 当前线程ID={Thread.CurrentThread.ManagedThreadId.ToString()} \r\n");
   Delay(1000);
  }
 }
 private Timer timerForm = new Timer(){Interval = 1000};
 private void button1_Click(object sender, EventArgs e)
 {
  textBox1.AppendText("button " + Thread.CurrentThread.ManagedThreadId.ToString() + "\r\n");
  timerForm.Start();
 }
 public static void Delay(int mimillisecond)
 {
  int start = Environment.TickCount;
  while (Math.Abs(Environment.TickCount - start) < mimillisecond)
  {
   System.Windows.Forms.Application.DoEvents();
  }
 }

System.Timers.Timer Class

是对System.Threading.Timer的一层封装,都是通过委托方法TimerCallback进行回调触发定时器事件,可以先看看System.Timers.Timer的代码实现方式:

  if (!value)
  {
  if (this.timer != null)
  {
   this.cookie = (object) null;
   this.timer.Dispose();
   this.timer = (System.Threading.Timer) null;
  }
  this.enabled = value;
  }
  else
  {
  this.enabled = value;
  if (this.timer == null)
  {
   if (this.disposed)
   throw new ObjectDisposedException(this.GetType().Name);
   int dueTime = (int) Math.Ceiling(this.interval);
   this.cookie = new object();
   this.timer = new System.Threading.Timer(this.callback, this.cookie, dueTime, this.autoReset ? dueTime : -1);
  }
  else
   this.UpdateTimer();
  }

不过 System.Threading.Timer的属性和方法都更加友善,我通常在使用中不设计更新界面,都会使用这个定时器类,有一点要说明的是,将SynchronizingObject属性赋值到控件后,事件中代码会在控件上委托调用,如timer.SynchronizingObject = this;可以看下System.Timers.Timer内部是如何实现的。

if (elapsedEventHandler != null)
  {
   if (this.SynchronizingObject != null && this.SynchronizingObject.InvokeRequired)
   {
   this.SynchronizingObject.BeginInvoke(elapsedEventHandler, new object[]
   {
    this,
    elapsedEventArgs
   });
   }
   else
   {
   elapsedEventHandler(this, elapsedEventArgs);
   }
  }

虽然System.Timers.Timer定时器理论上是不受单线程限制,可以短时间内触发多次,但是实际上会受到线程池的限制,先看巨硬对于此的说明:

如果 nullSynchronizingObject 属性,则在 ThreadPool 线程上引发 Elapsed 事件。 如果 Elapsed 事件的处理持续时间超过 Interval,则可能会在其他 ThreadPool 线程上再次引发该事件。 在这种情况下,事件处理程序应该是可重入的。

1、当SynchronizingObject不为null,将在指定的对象线程上触发事件,为单线程触发,与System.Windows.Forms.Timer执行方式相同;

2、当SynchronizingObject不为null时将在线程池(ThreadPool)上引发事件,执行事件内的代码。理论上可以重复载入,但是会受到ThreadPool线程数限制,比如ThreadPool.SetMaxThreads(8, 8),那么定时器触发事件只能同时载入8次;

4、后记

我现在用定时器基本上都是用System.Timers.Timer,在我看来System.Timers.Timer可以用SynchronizingObject属性实现在主线程运行,也可以不设置SynchronizingObject属性,是事件在线程池里触发,作为后台线程使用,基本能满足我在开发中的使用需求。

参考资料

System.Timers Namespace

System.Windows.Forms

System.Threading.ThreadPool Class

以上就是c# 区分几种定时器(timer)的详细内容,更多关于c# 定时器(timer)的资料请关注猪先飞其它相关文章!

[!--infotagslink--]

相关文章

  • C#实现简单的登录界面

    我们在使用C#做项目的时候,基本上都需要制作登录界面,那么今天我们就来一步步看看,如果简单的实现登录界面呢,本文给出2个例子,由简入难,希望大家能够喜欢。...2020-06-25
  • 浅谈C# 字段和属性

    这篇文章主要介绍了C# 字段和属性的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下...2020-11-03
  • C#中截取字符串的的基本方法详解

    这篇文章主要介绍了C#中截取字符串的的基本方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-11-03
  • C#连接SQL数据库和查询数据功能的操作技巧

    本文给大家分享C#连接SQL数据库和查询数据功能的操作技巧,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友参考下吧...2021-05-17
  • C#实现简单的Http请求实例

    这篇文章主要介绍了C#实现简单的Http请求的方法,以实例形式较为详细的分析了C#实现Http请求的具体方法,需要的朋友可以参考下...2020-06-25
  • C#中new的几种用法详解

    本文主要介绍了C#中new的几种用法,具有很好的参考价值,下面跟着小编一起来看下吧...2020-06-25
  • 使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序)

    这篇文章主要介绍了使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • C#开发Windows窗体应用程序的简单操作步骤

    这篇文章主要介绍了C#开发Windows窗体应用程序的简单操作步骤,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-04-12
  • C#从数据库读取图片并保存的两种方法

    这篇文章主要介绍了C#从数据库读取图片并保存的方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2021-01-16
  • C#和JavaScript实现交互的方法

    最近做一个小项目不可避免的需要前端脚本与后台进行交互。由于是在asp.net中实现,故问题演化成asp.net中jiavascript与后台c#如何进行交互。...2020-06-25
  • 经典实例讲解C#递归算法

    这篇文章主要用实例讲解C#递归算法的概念以及用法,文中代码非常详细,帮助大家更好的参考和学习,感兴趣的朋友可以了解下...2020-06-25
  • C++调用C#的DLL程序实现方法

    本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
  • 轻松学习C#的基础入门

    轻松学习C#的基础入门,了解C#最基本的知识点,C#是一种简洁的,类型安全的一种完全面向对象的开发语言,是Microsoft专门基于.NET Framework平台开发的而量身定做的高级程序设计语言,需要的朋友可以参考下...2020-06-25
  • C#变量命名规则小结

    本文主要介绍了C#变量命名规则小结,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-09
  • c#中(&&,||)与(&,|)的区别详解

    这篇文章主要介绍了c#中(&&,||)与(&,|)的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
  • C#绘制曲线图的方法

    这篇文章主要介绍了C#绘制曲线图的方法,以完整实例形式较为详细的分析了C#进行曲线绘制的具体步骤与相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • C# 中如何取绝对值函数

    本文主要介绍了C# 中取绝对值的函数。具有很好的参考价值。下面跟着小编一起来看下吧...2020-06-25
  • c#自带缓存使用方法 c#移除清理缓存

    这篇文章主要介绍了c#自带缓存使用方法,包括获取数据缓存、设置数据缓存、移除指定数据缓存等方法,需要的朋友可以参考下...2020-06-25
  • C#学习笔记- 随机函数Random()的用法详解

    下面小编就为大家带来一篇C#学习笔记- 随机函数Random()的用法详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • C#中list用法实例

    这篇文章主要介绍了C#中list用法,结合实例形式分析了C#中list排序、运算、转换等常见操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25