.NET Framework中定时器timer的单线程与多线程使用讲解

 更新时间:2016年04月27日 10:53:10   作者:winnow  
这篇文章主要介绍了.NET Framework中定时器timer的单线程与多线程使用讲解,单线程定时器在性能方面不如多线程,但是可以做到线程安全,需要的朋友可以参考下

如果你需要使用规律的时间间隔重复执行一些方法,最简单的方式是使用定时器(timer)。与下边的例子相比,定时器可以便捷、高效地使用内存和资源:

new Thread (delegate() {
             while (enabled)
             {
              DoSomeAction();
              Thread.Sleep (TimeSpan.FromHours (24));
             }
            }).Start();

这不仅仅会永久占用一个线程,而且如果没有额外的代码,DoSomeAction每天都会发生在更晚的时间。定时器解决了这些问题。

.NET Framework 提供了 4 种定时器。下边两个类是通用的多线程定时器:

(1)System.Threading.Timer
(2)System.Timers.Timer
另外两个是专用的单线程定时器:

(3)System.Windows.Forms.Timer (Windows Forms 的定时器)
(4)System.Windows.Threading.DispatcherTimer (WPF 的定时器)
多线程定时器更加强大、精确并且更加灵活,而单线程定时器对于一些简单的更新 Windows Forms 和 WPF 控件的任务来说是安全的,并且更加便捷。

1.多线程定时器Permalink

System.Threading.Timer是最简单的多线程定时器:它仅仅有一个构造方法和两个普通方法(取悦于极简主义者,还有本书作者!)。在接下来的例子中,一个定时器在 5 秒钟之后调用Tick方法来打印 “ tick… “,之后每秒打印一次直到用户按下回车键:

using System;
using System.Threading;

class Program
{
 static void Main()
 {
  // 首次间隔 5000ms,之后间隔 1000ms
  Timer tmr = new Timer (Tick, "tick...", 5000, 1000);
  Console.ReadLine();
  tmr.Dispose();     // 停止定时器并执行清理工作
 }

 static void Tick (object data)
 {
  // 这里运行在一个线程池线程上
  Console.WriteLine (data);     // 打印 "tick..."
 }
}

之后可以通过调用Change方法来改变定时器的时间间隔。如果你希望定时器只触发一次,可以指定Timeout.Infinite作为构造方法的最后一个参数。

.NET Framework 在System.Timers命名空间下提供了另一个名字相同的定时器类。它只是封装了 System.Threading.Timer,并在使用完全相同的底层引擎的前提下提供额外的便利。下面是增加功能的简介:

(1)实现了Component,允许用于 Visual Studio 的设计器中。
(2)Interval属性代替了Change方法。
(3)Elapsed事件代替了回调委托。
(4)Enabled属性用于开始或停止定时器(默认值是false)。
(5)Start和Stop方法,避免对Enabled属性感到困惑。
(6)AutoReset标识来指定是否为可重复的事件(默认为true)。
SynchronizingObject属性提供Invoke和BeginInvoke方法,用于在 WPF 和 Windows Forms 控件上安全调用方法。
这有个例子:

using System;
using System.Timers;  // 命名空间是 Timers 而不是 Threading

class SystemTimer
{
 static void Main()
 {
  Timer tmr = new Timer();    // 无需任何参数
  tmr.Interval = 500;
  tmr.Elapsed += tmr_Elapsed;  // 使用事件代替委托
  tmr.Start();          // 开启定时器
  Console.ReadLine();
  tmr.Stop();          // 停止定时器
  Console.ReadLine();
  tmr.Start();          // 重启定时器
  Console.ReadLine();
  tmr.Dispose();         // 永久停止定时器
 }

 static void tmr_Elapsed (object sender, EventArgs e)
 {
  Console.WriteLine ("Tick");
 }
}

多线程定时器使用线程池来允许少量线程服务多个定时器。这意味着,回调方法或Elapsed事件每次可能会在不同的线程上触发。此外,不论之前的Elapsed是否完成执行,Elapsed总是几乎按时触发。因此,回调方法或事件处理器必须是线程安全的。

多线程定时器的精度依赖于操作系统,通常是在 10-20 ms 的区间。如果需要更高的精度,你可以使用本地互操作(native interop)来调用 Windows 多媒体定时器,可以让精度提升到 1 ms。它定义在 winmm.dll 中,首先调用timeBeginPeriod来通知操作系统你需要更高的定时器精度,然后调用timeSetEvent来启动多媒体定时器。当使用完成后,调用timeKillEvent停止定时器,最后调用timeEndPeriod通知操作系统你不在需要更高的定时器精度了。可以通过搜索关键字 dllimport winmm.dll timesetevent 在网上找到完整的例子。

2.单线程定时器Permalink

.NET Framework 提供了两个定时器,为消除WPF 和 Windows Forms 应用程序的线程安全问题而设计:

System.Windows.Threading.DispatcherTimer(WPF)
System.Windows.Forms.Timer(Windows Forms)
单线程定时器不是被设计成能在其特定的环境外工作的。例如,如果在 Windows 系统服务应用程序中使用 Windows Forms 定时器,Timer事件不会触发!

它们暴露的成员都像System.Timers.Timer一样(Interval、Tick、Start和Stop),并且用法也类似。但是不同之处在于其内部是如何工作的。它们不是使用线程池来产生定时器事件,WPF 和 Windows Forms 定时器依赖于 UI 模型的底层消息循环机制(message pumping mechanism)。意味着Tick事件总是在创建该定时器的那个线程触发,在通常的程序中,它也就是管理所有 UI 元素和控件的那个线程。这有很多好处:

单线程计时器比较安全,对于更新 Windows Forms controls或者WPF这种简单任务来说更方便。在WPF或Windows Forms中安全的调用方法的SynchronizingObject对象。
单线程计时器是被设计成属于他们执行环境的计时器,如果你在一个Windows服务应用程序中使用Windows Forms的Timer,timer 事件并不会被触发,只有在对应的环境下才会被触发。
像System.Timers.Timer一样,他们也提供了相同的成员(Interval,Tick,Start,Stop),但是他们内部的工作原理不同,WPF和Windows Forms的计时器使用消息循环机制来取代线程池产生消息的机制。

你可以不必考虑线程安全。
新的Tick在之前的Tick完成执行前不会触发。
你可以直接在Tick时间事件的处理代码中更新 UI 控件,而不需要调用Control.Invoke或Dispatcher.Invoke。
这听起来好的难以置信,直到你意识到使用这些定时器的程序并不是真正的多线程,不会有并行执行。一个线程服务于所有定时器,并且还处理 UI 事件。这带来了单线程定时器的缺点:

除非Tick事件处理器执行的很快,否则 UI 会失去响应。
这使得 WPF 和 Windows Forms 定时器仅适用于小任务,通常就是那些更新 UI 外观的任务(例如,显示时钟或倒计时)。否则,你就需要多线程定时器。

在精度方面,单线程定时器与多线程定时器类似(几十毫秒),但是通常精度更低,因为它们会被其它 UI 请求(或其它定时器事件)推迟。

单线程计时器基于Windows消息循环,应用程序会同步的处理计时器的消息。会发现UI界面相应速度比较慢。解决这个问题的方法是使用多线程计时器。
单线程计时器的缺点:除非Tick事件的处理代码执行的非常快,否则UI界面会变得响应很慢。所以 WPF和Windows Forms的计时器都非常适合小任务,尤其是界面更新的任务。例如时钟和计数显示。否则,你需要一个多线程计时器


相关文章

  • ASP.NET Core实现文件上传和下载

    ASP.NET Core实现文件上传和下载

    这篇文章主要为大家详细介绍了ASP.NET Core实现文件上传和下载,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-07-07
  • 使用Visual Studio 2017写静态库

    使用Visual Studio 2017写静态库

    这篇文章主要为大家详细介绍了Visual Studio2017写静态库的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-04-04
  • c# .net 生成图片验证码的代码

    c# .net 生成图片验证码的代码

    c# .net 生成图片验证码的代码...
    2007-03-03
  • asp.net中穿透Session 0 隔离(二)

    asp.net中穿透Session 0 隔离(二)

    上一篇我们已经对Session 0 隔离有了进一步认识,如果在开发过程中确实需要服务与桌面用户进行交互,可以通过远程桌面服务的API 绕过Session 0 的隔离完成交互操作
    2012-11-11
  • ASP.NET 页生命周期概述(小结)

    ASP.NET 页生命周期概述(小结)

    了解个掌握ASP.NET页面生命周期是非常必要的,这有助于我们更加灵活的控制页面,以我们需要的方式编程开发。本文详细的介绍了ASP.NET 页生命周期,感兴趣的可以了解一下
    2018-11-11
  • ASP.NET MVC 项目直接预览PDF文件

    ASP.NET MVC 项目直接预览PDF文件

    本文主要介绍了ASP.NET MVC项目实现直接预览PDF文件的方法,具有很好的参考价值,下面跟着小编一起来看下吧
    2017-02-02
  • asp.net 获取指定文件夹下所有子目录及文件(树形)

    asp.net 获取指定文件夹下所有子目录及文件(树形)

    asp.net获取指定文件夹下所有子目录及文件,现在有asp.net的站的就是用这样的原理显示文件的
    2008-07-07
  • Cookies的各方面知识(基础/高级)深度了解

    Cookies的各方面知识(基础/高级)深度了解

    Cookies想必所有人都了解本文将围绕Cookies基础知识(什么是Cookies/Cookies如何传递/Cookies如何存储/Cookies如何查看)Cookies高级知识/Cookie的限制等等方方面面深入了解,感兴趣的朋友可以参考下,或许对你学习cookies有所帮助
    2013-02-02
  • swagger上传文件并支持jwt认证的实现方法

    swagger上传文件并支持jwt认证的实现方法

    今天通过本文给大家分享swagger上传文件并支持jwt认证的实现方法,文中提到了安装方法及实现代码,感兴趣的朋友跟随脚本之家小编一起学习吧
    2018-05-05
  • Winform OpenFileDialog打开文件对话框

    Winform OpenFileDialog打开文件对话框

    这篇文章主要为大家详细介绍了Winform OpenFileDialog打开文件对话框 的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-03-03

最新评论