在.NET Framework里面提供了三种Timer:

① System.Windows.Forms.Timer
(运行在主线程上,通过Tick事件触发)

② System.Timers.Timer
(可以多线程,也可以单线程,通过Elapsed事件触发)

③ System.Threading.Timer

  1. System.Windows.Forms.Timer:基于窗体程序,直接拖拽到窗体上。
    属性:
    Interval:间隔触发的时间。
    Enabled:true表示启用,false表示禁止
  private void WriteInfo(string str)
  {
       if (txt_Info.InvokeRequired)
       {
           this.Invoke(new Action(() => { txt_Info.AppendText(str + "\r\n"); }));
       }
       else
       {
           txt_Info.AppendText(str + "\r\n");
       }
  }

  private void btn_Start_Click(object sender, EventArgs e)
  {
       if (btn_Start.Text == "开始")
       {
            btn_Start.Text = "停止";
            Application.DoEvents();
            timer1.Enabled = true;
            btn_Start.Text = "开始";
       }
   }

   private void timer1_Tick(object sender, EventArgs e)
   {
        for (int i = 0; i < 10; i++)
        {
            Thread.Sleep(1000);
            WriteInfo("Count: " + i + "\t  id:" + Thread.CurrentThread.ManagedThreadId);
        }
   }

结果:
在这里插入图片描述
总结:

  1. 从上面的运行结果可以看到,窗体Timer 都是运行在同一个线程上主线程),所以会卡UI 。
  2. 单线程执行的优点就是,不需要关心线程安全,也就是说,一段程序从头至尾都是由一个线程完成。
  3. Form下的Timer用于对时间精度要求不高的地方。

2. System.Timers.Timer:只能通过声明对象的方式使用,用于对时间精度有要求的地方。
属性:
AutoReset:true表示事件关联的方法只执行一次,false表示一直执行。
SynchronizingObject:此timer默认是运行在线程池的,也就是说CPU自动分配线程,但是我们可以使用SynchronizingObject属性指定唯一线程。

		private System.Timers.Timer tmr = new System.Timers.Timer();
        private object obj = new object();

        public Timer_timer()
        {
            InitializeComponent();
         
            tmr.Elapsed += new System.Timers.ElapsedEventHandler(OnTmrTrg);
            tmr.Interval = 100;
            tmr.AutoReset = true; //true-一直循环 ,false-循环一次   
            // tmr.SynchronizingObject = this; //运行在主线程上
            tmr.Enabled = false;
        }
        private void OnTmrTrg(object sender, System.Timers.ElapsedEventArgs e)
        {
            WriteInfo("tmr loop:  " + Thread.CurrentThread.ManagedThreadId);
            lock (obj)
            {
                WriteInfo("tmr is running :" + Thread.CurrentThread.ManagedThreadId);
                for (int i = 0; i < 10; i++)
                {
                    Thread.Sleep(1000);
                    WriteInfo("Count: " + i + ": " + Thread.CurrentThread.ManagedThreadId);
                }
            }
        }
        private void WriteInfo(string str)
        {
            if (txt_Info.InvokeRequired)
            {
                this.Invoke(new Action(() => { txt_Info.AppendText(str + "\r\n"); }));
            }
            else
            {
                txt_Info.AppendText(str + "\r\n");
            }
        }
        private void btn_Enter_Click(object sender, EventArgs e)
        {
            if (btn_Enter.Text == "确定")
            {
                btn_Enter.Text = "取消";
                Application.DoEvents();
         
                tmr.Start();
            }
            else
            {
                btn_Enter.Text = "确定";
                tmr.Stop();
            }
        }

结果:在未指定SynchronizingObject属性的时候默认是多线程运行的,运行如图:
多线程执行
通过 SynchronizingObject = this 将指定主线程,运行结果如下
单线程执行
总结:

  1. Timer.timer是通过主线程创建,执行在线程池上的,所以默认在不指定SynchronizingObject属性的情况下,不卡主界面。
  2. 由于可以多线程执行,所以需要考虑线程安全(假设一种情况,A线程正在计算余额(总金额-消费金额),这个时候然后B线程将总金额改了,那A线程所返回的会发生错误)。解决方案有两种:1. 金额的计算工作全部由A线程执行(单线程)。2. 使用Lock () 确保每次A线程执行计算任务的时候B线程等待A完成在进行。
  3. Timer.timer可以通过AutoReset = false 设置,只执行一次事件方法。
  4. Start ()&Stop () 和 Enabled = true/false等效。

System.Threading.Timer:同样是运行在线程池,但是此timer是以回调的方式来实现定时任务,

    public partial class Threading_Timer : Form
    {
        public System.Threading.Timer tmr;
        public static int count = 0;

        public Threading_Timer()
        {
            InitializeComponent();
           
        }

        private void btn_Start_Click(object sender, EventArgs e)
        {
            if (btn_Start.Text == "开始")
            {
                btn_Start.Text = "结束";
                tmr = new System.Threading.Timer(new System.Threading.TimerCallback((obj) =>
                {
                    Tmp tmp = (Tmp)obj;
                    WriteInfo(tmp.info + ":" + count++);

                }), new Tmp() { info = "Count: " }, 0, 1000);
            }
            else
            {
                btn_Start.Text = "开始";
                tmr.Dispose();
            }
        }
        private void WriteInfo(string str)
        {
            if (txt_Info.InvokeRequired)
            {
                this.Invoke(new Action(() => { txt_Info.AppendText(str + "\r\n"); }));
            }
            else
            {
                txt_Info.AppendText(str + "\r\n");
            }
        }
    }
    public class Tmp
    {
        public string info { get; set; }
    }

运行结果:
Threading.Timer
总结:

  1. 运行于线程池,不卡主界面。
  2. 没有Start () 和 Stop () 方法控制启动停止。在对象构造的时候启动,需要使用Dispose () 方法停止执行。
  3. 构造函数参数列表: new timer ( new TimerCallback ( callBack), 回调函数传参对象 , 启动时间 , 间隔时间)。
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐