项目中用到了定时器随着服务启动作定时任务,按指定的准点时间定时执行相关操作,但是在指定准点时间内我只想让它执行一次,要避免重入问题的发生。

首先简单介绍一下timer,这里所说的timer是指的System.Timers.timer,顾名思义,就是可以在指定的间隔是引发事件。官方介绍在这里,摘抄如下:


Timer 组件是基于服务器的计时器,它使您能够指定在应用程序中引发 Elapsed 事件的周期性间隔。然后可通过处理这个事件来提供常规处理。 例如,假设您有一台关键性服务器,必须每周 7 天、每天 24 小时都保持运行。 可以创建一个使用 Timer 的服务,以定期检查服务器并确保系统开启并在运行。 如果系统不响应,则该服务可以尝试重新启动服务器或通知管理员。    基于服务器的 Timer 是为在多线程环境中用于辅助线程而设计的。     服务器计时器可以在线程间移动来处理引发的 Elapsed 事件,这样就可以比 Windows 计时器更精确地按时引发事件。

那使用这个计时器有啥好处呢?主要因为它是通过.NET Thread Pool实现的、轻量、计时精确、对应用程序及消息没有特别的要求。

Timer是怎么使用的之前有写过这篇:C# System.Timers.Timer定时器的使用和定时自动清理内存应用

什么叫重入呢?这是一个有关多线程编程的概念:程序中,多个线程同时运行时,就可能发生同一个方法被多个进程同时调用的情况。当这个方法中存在一些非线程安全的代码时,方法重入会导致数据不一致的情况。Timer方法重入是指使用多线程计时器,一个Timer处理还没有完成,到了时间,另一Timer还会继续进入该方法进行处理。

关于定时器的重入问题解决方法尝试如下

1、使用锁lock(Object)的方法来防止重入,表示一个Timer处理正在执行,下一个Timer发生的时候发现上一个没有执行完就等待执行,适用重入很少出现的场景。在触发的方法中加入lock,这样当线程2进入触发的方法中,发现已经被锁,会等待锁中的代码处理完在执行,代码如下:

private static System.Timers.Timer aTimer = new System.Timers.Timer(); private static object loker=new object();   /// <summary>        /// 设置定时器        /// </summary>        public static void SetTimer()        {            //读取配置时间            try            {                aTimer.Interval = 30000;                aTimer.Elapsed += new System.Timers.ElapsedEventHandler(OnTimedEvent);                aTimer.AutoReset = true;//每到指定时间Elapsed事件是到时间就触发                aTimer.Enabled = true; //指示 Timer 是否应引发 Elapsed 事件。            }            catch (Exception ex)            {                LogManager.RecordLog(LogType.Error, "ipad数据同步出错:" +ex.Message,ex);            }        }        private static void OnTimedEvent(Object source, ElapsedEventArgs e)        {            //如果当前时间是为配置的准点时间就进来            var time =Convert.ToInt32( SynchronousHelper.AppConfig.get_Items("SycTime"));            if (DateTime.Now.Hour == time && DateTime.Now.Minute == 0)            {                //lock,这样当线程2进入触发的方法中,发现已经被锁,会等待锁中的代码处理完在执行                lock (loker)                {                    LogManager.RecordLog(LogType.Info, "数据开始同步时间:" + e.SignalTime, null);                    SetTimerStart();                    System.Threading.Thread.Sleep(60000); //执行完越过当前分钟,使整点内只能进来一次                }            }        }

2.置一个标志,表示一个Timer处理正在执行,下一个Timer发生的时候发现上一个没有执行完就放弃(注意这里是放弃,而不是等待哦,看看执行结果就明白啥意思了)执行,适用重入经常出现的场景。在多线程下给inTimer赋值不够安全,Interlocked.Exchange提供了一种轻量级的线程安全的给对象赋值的方法(感觉比较高上大,也是比较推荐的一种方法)。

private static System.Timers.Timer aTimer = new System.Timers.Timer(); private static int inTimer = 0;        /// <summary>        /// 设置定时器        /// </summary>        public static void SetTimer()        {            //读取配置时间            try            {                aTimer.Interval = 30000; //半分钟触发一次                aTimer.Elapsed += new System.Timers.ElapsedEventHandler(OnTimedEvent);                aTimer.AutoReset = true;//每到指定时间Elapsed事件是到时间就触发                aTimer.Enabled = true; //指示 Timer 是否应引发 Elapsed 事件。            }            catch (Exception ex)            {                LogManager.RecordLog(LogType.Error, "ipad数据同步出错:" +ex.Message,ex);            }        }        private static void OnTimedEvent(Object source, ElapsedEventArgs e)        {            //如果当前时间是为配置的准点时间就进来            var time =Convert.ToInt32( SynchronousHelper.AppConfig.get_Items("SycTime"));            if (DateTime.Now.Hour == time && DateTime.Now.Minute == 0)            {                //inTimer设置一个标志,表示一个Timer处理正在执行,下一个Timer发生的时候发现上一个没有执行完就放弃                if (Interlocked.Exchange(ref inTimer, 1) == 0)                {                    LogManager.RecordLog(LogType.Info, "数据开始同步时间:" + e.SignalTime, null);                    SetTimerStart();                    System.Threading.Thread.Sleep(60000); //执行完等待越过当前分钟,使整点内只能进来一次                    Interlocked.Exchange(ref inTimer, 0);                }            }        }

稍微总结一下,timer是一个使用挺简单的类,拿来即用,这里主要总结了使用timer时重入问题的解决,以前也没思考过这个问题,解决方案也挺简单。这里的解决方案同时也适用多线程的重入问题。

更多相关文章

  1. C#如何计算传入的时间距离今天的时间差的实例分享
  2. C#多线程之Semaphore的使用详解
  3. C# WinForm跨线程访问控件的图文详解
  4. C++11多线程编程基础入门
  5. C++解决方法:多线程同步经典案例之生产者消费者问题
  6. 如何用Perl表示当前时间
  7. 递归算法的时间复杂度是什么
  8. .NET中async异步、thread多线程
  9. C++如何简单输出当前日期时间

随机推荐

  1. 六一儿童节,送你一个F字符串
  2. 每周 10 道 Java 面试题:集合类
  3. 学习linux的第5天
  4. 如何使用maven 轻松重构项目
  5. iPhone要降价?巨头战争很激烈
  6. 【Java】几道让你拿offer的面试题
  7. 基础语法_Haskell笔记1
  8. 骗局!卸载搜狗输入法教程
  9. 【重磅】Google或借道搜狗回归中国,明天见
  10. Core Java 并发:理解并发概念