聊透多线程编程-线程池-7.C# 三个Timer类

打印 上一主题 下一主题

主题 1713|帖子 1713|积分 5139

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
目次
1. System.Threading.Timer
2. System.Timers.Timer
3. System.Windows.Forms.Timer
总结对比
System.Timers.Timer 更新 UI的示例
方法1:利用 SynchronizationContext 更新 UI
方法2:利用 利用 Control.Invoke 更新 UI

 
今天要说的这三个Timer类分别是System.Threading.Timer 、System.Timers.Timer和System.Windows.Forms.Timer,其中前两个都是在线程池线程上运行的,而System.Windows.Forms.Timer则是运行在UI线程的,之以是将第三个与前两个放在一起比力,是因为他们比力像,平时如果不留意的话,轻易搞混淆。

1. System.Threading.Timer

System.Threading.Timer 是 .NET 中提供的一个轻量级、高性能的计时器,实用于必要正确控订定时任务的场景。它通过回调函数的方式工作,没有事件模型。
特点:


  • 底层实现:最轻量级的计时器,直接基于线程池运行。
  • 回调机制:通过回调函数执行任务,没有事件模型。
  • 机动性:支持动态调整定时器的举动(如延迟启动、重复触发等)。
  • 线程模型:在 线程池线程 上运行,得当后台任务。
  • 非常处理:必要手动捕捉和处理非常,否则可能导致程序崩溃。
实用场景:


  • 高性能、低级别的计时需求。
  • 必要完全控制计时器举动的场景。
  • 不必要与 UI 或特定线程交互的任务。
示例代码:
  1. using System;
  2. using System.Threading;
  3. class Program
  4. {
  5.     static void Main()
  6.     {
  7.         Timer timer = new Timer(TimerCallback, null, 0, 1000); // 第一次立即触发,之后每秒触发一次
  8.         Console.WriteLine("按 Enter 键退出...");
  9.         Console.ReadLine();
  10.     }
  11.     private static void TimerCallback(object state)
  12.     {
  13.         Console.WriteLine("Callback: " + DateTime.Now);
  14.     }
  15. }
复制代码
缺点:


  • 无事件模型:不支持事件驱动开辟,利用起来不敷直观。
  • 非常风险:如果回调函数中抛出未捕捉的非常,可能会导致程序崩溃。
  • UI 线程限定:不能直接更新 UI,需额外处理线程同步标题。
留意事项:


  • 在回调函数中避免长时间壅闭操纵,以免影响线程池性能。
  • 如果必要与 UI 线程交互,应利用 SynchronizationContext 或其他线程同步机制。
  • 利用完成后必要调用 Dispose() 方法释放资源。

2. System.Timers.Timer

System.Timers.Timer 是基于 System.Threading.Timer 构建的一个更高条理的计时器,提供了事件驱动模型,得当必要定期执行后台任务的应用程序。

特点:


  • 高层封装:基于 System.Threading.Timer 实现,提供了更高级别的抽象。
  • 事件驱动:利用 Elapsed 事件处理定时任务。
  • 易用性:支持属性(如 Interval、AutoReset),可以动态调整计时器举动。
  • 线程模型:默认在 线程池线程 上运行,但可以通过 SynchronizingObject 属性绑定到特定线程(如 UI 线程)。
  • 非常处理:Elapsed 事件中的非常会被自动捕捉并记录,不会导致程序崩溃。
实用场景:


  • 必要定期执行后台任务的应用程序。
  • 对线程安全性和易用性要求较高的场景。
  • 必要跨线程通信或与 UI 线程交互的任务。
示例代码:
  1. using System;
  2. using System.Timers;
  3. class Program
  4. {
  5.     static void Main()
  6.     {
  7.         Timer timer = new Timer(1000); // 每秒触发一次
  8.         timer.Elapsed += OnTimedEvent; // 绑定事件处理程序
  9.         timer.AutoReset = true;       // 是否重复触发
  10.         timer.Enabled = true;         // 启动计时器
  11.         Console.WriteLine("按 Enter 键退出...");
  12.         Console.ReadLine();
  13.     }
  14.     private static void OnTimedEvent(object sender, ElapsedEventArgs e)
  15.     {
  16.         Console.WriteLine($"Elapsed at {e.SignalTime}");
  17.     }
  18. }
复制代码
缺点:


  • 精度略低:由于其高层封装,可能存在一定的延迟。
  • 依赖线程池:仍基于线程池运行,可能在高负载情况下受到影响。
  • 资源管理:如果不正确释放资源,可能会导致内存泄漏。
留意事项:


  • 如果必要与 UI 线程交互,记得设置 SynchronizingObject 属性。
  • 利用完成后必要调用 Dispose() 方法释放资源。
  • 在 Elapsed 事件处理程序中避免长时间壅闭操纵。

3. System.Windows.Forms.Timer
 


System.Windows.Forms.Timer 是专门为 Windows 窗体应用程序计划的计时器,它在 UI 线程上运行,得当用于更新 UI 控件。
特点:


  • UI 线程运行:在 UI 线程上运行,可以直接更新 UI 控件。
  • 简单易用:提供 Tick 事件,易于利用。
  • 无多线程支持:不得当后台任务或多线程情况。
  • 低精度:由于运行在 UI 线程上,受 UI 响应速度的影响,精度较低。
实用场景:


  • Windows 窗体应用程序中必要定期更新 UI 的场景。
  • 对计时精度要求不高的任务。
  • 不必要后台线程支持的简单定时任务。
示例代码:
  1. using System;
  2. using System.Windows.Forms;
  3. using static System.Net.Mime.MediaTypeNames;
  4. class Program : Form
  5. {
  6.     static void Main()
  7.     {
  8.         Application.Run(new Program());
  9.     }
  10.     public Program()
  11.     {
  12.         Timer timer = new Timer();
  13.         timer.Interval = 1000; // 设置间隔时间(毫秒)
  14.         timer.Tick += Timer_Tick; // 绑定事件处理程序
  15.         timer.Start();
  16.     }
  17.     private void Timer_Tick(object sender, EventArgs e)
  18.     {
  19.         Console.WriteLine("Tick: " + DateTime.Now);
  20.     }
  21. }
复制代码
缺点:


  • 低精度:受 UI 线程响应速度的影响,不得当高精度计时。
  • 单线程限定:只能在 UI 线程上运行,无法用于后台任务。
  • 性能瓶颈:如果 UI 线程被壅闭,计时器也会停止工作。
留意事项:


  • 不要在 Tick 事件中执行耗时操纵,以免壅闭 UI 线程。
  • 仅实用于 Windows 窗体应用程序,不实用于其他类型的应用程序(如控制台应用或 ASP.NET 应用)。
  • 如果必要更高的计时精度或后台任务支持,请选择其他计时器。

总结对比

 
计时器类型

运行线程

易用性

精度

实用场景

System.Threading.Timer

线程池线程

较低


高性能、低级别计时需求

System.Timers.Timer

线程池线程

较高


定期执行后台任务

System.Windows.Forms.Timer

UI 线程

非常高


更新 Windows 窗体 UI


System.Timers.Timer 更新 UI的示例

方法1:利用 SynchronizationContext 更新 UI

  1. using System;
  2. using System.Reflection.Emit;
  3. using System.Timers;
  4. using System.Windows.Forms;
  5. using static System.Net.Mime.MediaTypeNames;
  6. class Program : Form
  7. {
  8.     private Label label;
  9.     private Timer timer;
  10.     private SynchronizationContext uiContext;
  11.     static void Main()
  12.     {
  13.         Application.Run(new Program());
  14.     }
  15.     public Program()
  16.     {
  17.         // 初始化 UI
  18.         label = new Label
  19.         {
  20.             Text = "等待计时器更新...",
  21.             AutoSize = true,
  22.             Location = new System.Drawing.Point(10, 10)
  23.         };
  24.         this.Controls.Add(label);
  25.         // 保存 UI 线程的上下文
  26.         uiContext = SynchronizationContext.Current;
  27.         // 初始化计时器
  28.         timer = new Timer(1000); // 每秒触发一次
  29.         timer.Elapsed += OnTimedEvent;
  30.         timer.AutoReset = true;
  31.         timer.Enabled = true;
  32.     }
  33.     private void OnTimedEvent(object sender, ElapsedEventArgs e)
  34.     {
  35.         // 使用 SynchronizationContext 切换到 UI 线程
  36.         uiContext.Post(_ =>
  37.         {
  38.             label.Text = $"更新时间: {DateTime.Now}";
  39.         }, null);
  40.     }
  41. }
复制代码
方法2:利用 利用 Control.Invoke 更新 UI

  1. using System;
  2. using System.Reflection.Emit;
  3. using System.Timers;
  4. using System.Windows.Forms;
  5. using static System.Net.Mime.MediaTypeNames;
  6. class Program : Form
  7. {
  8.     private Label label;
  9.     private Timer timer;
  10.     static void Main()
  11.     {
  12.         Application.Run(new Program());
  13.     }
  14.     public Program()
  15.     {
  16.         // 初始化 UI
  17.         label = new Label
  18.         {
  19.             Text = "等待计时器更新...",
  20.             AutoSize = true,
  21.             Location = new System.Drawing.Point(10, 10)
  22.         };
  23.         this.Controls.Add(label);
  24.         // 初始化计时器
  25.         timer = new Timer(1000); // 每秒触发一次
  26.         timer.Elapsed += OnTimedEvent;
  27.         timer.AutoReset = true;
  28.         timer.Enabled = true;
  29.     }
  30.     private void OnTimedEvent(object sender, ElapsedEventArgs e)
  31.     {
  32.         // 使用 Control.Invoke 切换到 UI 线程
  33.         if (label.InvokeRequired)
  34.         {
  35.             label.Invoke(new Action(() =>
  36.             {
  37.                 label.Text = $"更新时间: {DateTime.Now}";
  38.             }));
  39.         }
  40.         else
  41.         {
  42.             label.Text = $"更新时间: {DateTime.Now}";
  43.         }
  44.     }
  45. }
复制代码
关键点剖析
线程标题:
        System.Timers.Timer 的 Elapsed 事件运行在 线程池线程 上。
        直接从非 UI 线程更新 UI 会导致非常,因此必要切换到 UI 线程。
解决方案:
        SynchronizationContext:用于捕捉和存储当火线程(通常是 UI 线程)的上下文,并通过 Post 或 Send 方法将操纵切换回该线程。
        Control.Invoke:检查控件是否必要调用 Invoke,如果是,则通过 Invoke 将操纵委托给 UI 线程执行。
性能考虑:
        如果更新频率较高,发起减少不必要的线程切换操纵,以避免性能开销。
资源管理:
        在窗体关闭时,记得停止并释放 System.Timers.Timer,以避免潜在的内存泄漏或非常。

 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

曂沅仴駦

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表