>  기사  >  백엔드 개발  >  C#의 타이머 타이머 재진입 문제에 대한 솔루션

C#의 타이머 타이머 재진입 문제에 대한 솔루션

黄舟
黄舟원래의
2017-08-11 13:21:382658검색

프로젝트에서는 서비스가 시작되면 예약된 작업을 수행하기 위해 타이머를 사용하고 지정된 시간에 해당 작업이 수행됩니다. 그러나 재진입 문제를 피하기 위해 지정된 시간 내에 한 번만 실행되기를 원합니다.

먼저 타이머에 대해 간단히 소개하겠습니다. 여기서 언급하는 타이머는 이름에서 알 수 있듯이 지정된 간격으로 이벤트를 트리거할 수 있습니다. 공식 소개는 여기에 있고, 발췌문은 다음과 같습니다:


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

그럼 이 타이머를 사용하면 어떤 이점이 있을까요? 주로 .NET 스레드 풀을 통해 구현되고, 가볍고, 정확한 타이밍을 가지며, 애플리케이션과 메시지에 대한 특별한 요구 사항이 없기 때문입니다.

타이머를 사용하는 방법 이전에 C# 시스템 사용에 대한 기사를 작성한 적이 있습니다. 이는 다중 스레드 프로그래밍에 대한 개념입니다. 프로그램에서 여러 스레드가 동시에 실행될 때 동일한 메서드가 여러 프로세스에서 동시에 호출될 수 있습니다. 이 메서드에 스레드로부터 안전하지 않은 코드가 있는 경우 메서드 재진입으로 인해 데이터 불일치가 발생합니다. 타이머 방법 재진입은 다중 스레드 타이머의 사용을 의미합니다. 하나의 타이머 처리가 완료되지 않은 경우 시간이 다 되면 다른 타이머가 계속해서 처리 방법에 들어갑니다.

타이머 재진입 문제에 대해 다음 해결 방법을 시도해 보세요:

1. 재진입을 방지하려면 잠금(객체) 메서드를 사용하세요. 이는 타이머 처리가 실행 중임을 의미합니다. 실행 후 실행을 기다리기만 하면 됩니다. 이는 재진입이 거의 발생하지 않는 시나리오에 적합합니다. 스레드 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. 타이머 처리 중임을 나타내는 플래그를 설정합니다. 다음 타이머가 발생하면 이전 타이머가 실행되지 않은 것으로 확인됩니다. 포기 (참고로 이는 대기하는 것이 아니라 포기하는 것입니다. 이해하게 될 것입니다. 실행 결과를 보는 것을 의미함) 재진입이 자주 발생하는 시나리오에 적합합니다. 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);
                }
            }
        }

좀 요약하자면, 타이머는 사용하기가 매우 간단한 클래스이고 즉시 사용할 수 있습니다. 여기서는 타이머를 사용할 때 발생하는 재진입 문제에 대한 해결책과 그 해결책을 주로 요약합니다. 아주 간단합니다. 여기의 솔루션은 다중 스레드 재진입 문제에도 적용됩니다.

위 내용은 C#의 타이머 타이머 재진입 문제에 대한 솔루션의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.