51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

C# 解决Timer定时器在整点重复进入

在项目中遇到一个需求,在整点的时候执行一个方法。(保存数据、处理数据等),此时我们需要一个Timer定时器,当整点的时候可以指定特定事件。那如何解决Timer定时器在整点重复进入方法的问题,继续往下看。{#u8f9d7eb2}

这里我们使用一个实例:当运行到指定时间段的时候,文字开始滚动(实现跑马灯效果){#u22d65c03}

界面效果 {#wHXxR}

在Form窗体中,定义一个label标签,内容是:☆123☆{#u91bcc2df}

代码 {#OiODf}

using System;
using Timers = System.Timers;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;

namespace MyTimer
{
public partial class Form1 : Form
{
private Timers.Timer timer1;
private int inTimer;


        public Form1()
        {
            InitializeComponent();
            timer1 = new Timers.Timer()
            {
                Interval = 100,
                Enabled = true,
                AutoReset = true
                };
            timer1.Elapsed += Timer1_Elapsed;
            this.StartPosition = FormStartPosition.CenterScreen;
        }

        private void Timer1_Elapsed(object sender, Timers.ElapsedEventArgs e)
        {
            DateTime currentTime = DateTime.Now;
            if(currentTime.Second == 10 || currentTime.Second == 20 || currentTime.Second == 30 || currentTime.Second == 40 || currentTime.Second == 50)
            {
                #region 第一种方法
                    //inTimer设置一个标志,表示一个Timer处理正在执行,下一个Timer发生的时候发现上一个没有执行完就放弃
                    if (Interlocked.Exchange(ref inTimer, 1) == 0)
                    {
                        Debug.WriteLine($"数据开始同步时间:{e.SignalTime}");

                        //第一个重载是从当前索引开始截取后面的字符串 + 第二个重载是从当前索引开始,长度是多少
                        this.Invoke(new Action(() =>
                                           {
                                               lbl.Text = lbl.Text.Substring(1) + lbl.Text.Substring(0, 1);
                                           }));

                        System.Threading.Thread.Sleep(60000); //执行完等待越过当前分钟,使整点内只能进来一次
                        Interlocked.Exchange(ref inTimer, 0);
                    }
                #endregion

                    #region 第二种方法
                    if (Math.Abs(currentTime.Millisecond) < 80  )
                    {                
                        this.Invoke(new Action(() =>
                                           {
                                               lbl.Text = lbl.Text.Substring(1) + lbl.Text.Substring(0, 1);
                                           }));
                    }
                #endregion
                }
        }     
    }



`}`

{#uICEA}


代码解析 {#rXzxf}

从当前代码实例解析,首先我们可以知道在窗体初始化的时候给定时器设置了100ms的间隔,也就是说100ms定时器就会触发一次{#u71f1b9ab}

timer正常如果不做任何设置情况下,就是定时器在10,20,30,40,50S的时候会进入这个if判断,在这里面去执行自己的需求方法。此时问题出现:timer的间隔时间为100ms,可能会重复进入多次if语句去执行方法,但是实际需求就是只执行一次方法。{#ud615f5f1}

if(currentTime.Second == 10 || currentTime.Second == 20 || currentTime.Second == 30 || currentTime.Second == 40 || currentTime.Second == 50)

{#GjMZg}

第一种方法------设置标志位 {#k3lz6}

  1. 设置一个标志位,

private int inTimer;{#ufe77aaab}

  1. 当inTimer为1的时候表示Timer正在处理,如果此时下一个Timer进入这个判断发现没有执行完就放弃

if(Interlocked.Exchange(ref inTimer, 1) == 0 ){#uc67cf2e5}

  1. 执行整点需要运行的方法

  2. 此时可以设置一个延时,执行完越过整点,使整点只能进入一次

  3. 将inTimer标志位设置为初始状态0Interlocked.Exchange(ref inTimer, 0);

    #region 第一种方法 //inTimer设置一个标志,表示一个Timer处理正在执行,下一个Timer发生的时候发现上一个没有执行完就放弃 if (Interlocked.Exchange(ref inTimer, 1) == 0) { Debug.WriteLine($"数据开始同步时间:{e.SignalTime}");

     //第一个重载是从当前索引开始截取后面的字符串 + 第二个重载是从当前索引开始,长度是多少
     this.Invoke(new Action(() =>
                            {
                                lbl.Text = lbl.Text.Substring(1) + lbl.Text.Substring(0, 1);
                            }));
    
     System.Threading.Thread.Sleep(60000); //执行完等待越过当前分钟,使整点内只能进来一次
     Interlocked.Exchange(ref inTimer, 0);
    

    } #endregion

{#n7x07}


  • 优点:简单易懂,适用于简单的时间控制需求。
  • 缺点:可能不够精确,受系统时间的影响较大,不适用于需要精确控制的场景。

第二种方法------设置定时器前后进入的误差时间 {#XaUjs}

直接使用Math.Abs方法,判断当前毫秒是否小于80,因为timer的间隔为100,可以保证不会第二次进入。{#u61f6a72d}

#region 第二种方法
if (Math.Abs(currentTime.Millisecond) < 80  )
{                
    this.Invoke(new Action(() =>
                           {
                               lbl.Text = lbl.Text.Substring(1) + lbl.Text.Substring(0, 1);
                           }));
}
#endregion

{#JQgB7}

  • 优点:能够精确控制并发访问,避免竞态条件,适用于多线程环境下的任务调度。
  • 缺点:相对较复杂,需要考虑线程安全和并发问题,适用于需要精确控制的场景。
赞(0)
未经允许不得转载:工具盒子 » C# 解决Timer定时器在整点重复进入