[c#] Windows 서비스에서 사용하기위한 최적의 타이머

N 기간마다 실행할 Windows 서비스를 만들어야합니다.
질문 :
어떤 타이머 컨트롤을 사용해야합니까 : System.Timers.TimerSystem.Threading.Timer하나? 무언가에 영향을 미치나요?

나는 System.Timers.TimerWindows 서비스에서 정확하지 않은 작업에 대한 많은 증거를 들었 기 때문에 묻는 것입니다 .
감사합니다.



답변

System.Timers.TimerSystem.Threading.Timer서비스에 대해 작동합니다.

당신이 피하려는 타이머는 System.Web.UI.TimerSystem.Windows.Forms.TimerASP 애플리케이션과 윈폼에 대한 각각있는. 이를 사용하면 서비스가 빌드중인 애플리케이션 유형에 실제로 필요하지 않은 추가 어셈블리를로드하게됩니다.

System.Timers.Timer다음 예제와 같이 사용하십시오 (또한 Tim Robinson의 답변에 명시된 것처럼 가비지 수집을 방지하기 위해 클래스 수준 변수를 사용하는지 확인하십시오).

using System;
using System.Timers;

public class Timer1
{
    private static System.Timers.Timer aTimer;

    public static void Main()
    {
        // Normally, the timer is declared at the class level,
        // so that it stays in scope as long as it is needed.
        // If the timer is declared in a long-running method,  
        // KeepAlive must be used to prevent the JIT compiler 
        // from allowing aggressive garbage collection to occur 
        // before the method ends. (See end of method.)
        //System.Timers.Timer aTimer;

        // Create a timer with a ten second interval.
        aTimer = new System.Timers.Timer(10000);

        // Hook up the Elapsed event for the timer.
        aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);

        // Set the Interval to 2 seconds (2000 milliseconds).
        aTimer.Interval = 2000;
        aTimer.Enabled = true;

        Console.WriteLine("Press the Enter key to exit the program.");
        Console.ReadLine();

        // If the timer is declared in a long-running method, use
        // KeepAlive to prevent garbage collection from occurring
        // before the method ends.
        //GC.KeepAlive(aTimer);
    }

    // Specify what you want to happen when the Elapsed event is 
    // raised.
    private static void OnTimedEvent(object source, ElapsedEventArgs e)
    {
        Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
    }
}

/* This code example produces output similar to the following:

Press the Enter key to exit the program.
The Elapsed event was raised at 5/20/2007 8:42:27 PM
The Elapsed event was raised at 5/20/2007 8:42:29 PM
The Elapsed event was raised at 5/20/2007 8:42:31 PM
...
 */

를 선택 System.Threading.Timer하면 다음과 같이 사용할 수 있습니다.

using System;
using System.Threading;

class TimerExample
{
    static void Main()
    {
        AutoResetEvent autoEvent     = new AutoResetEvent(false);
        StatusChecker  statusChecker = new StatusChecker(10);

        // Create the delegate that invokes methods for the timer.
        TimerCallback timerDelegate = 
            new TimerCallback(statusChecker.CheckStatus);

        // Create a timer that signals the delegate to invoke 
        // CheckStatus after one second, and every 1/4 second 
        // thereafter.
        Console.WriteLine("{0} Creating timer.\n", 
            DateTime.Now.ToString("h:mm:ss.fff"));
        Timer stateTimer = 
                new Timer(timerDelegate, autoEvent, 1000, 250);

        // When autoEvent signals, change the period to every 
        // 1/2 second.
        autoEvent.WaitOne(5000, false);
        stateTimer.Change(0, 500);
        Console.WriteLine("\nChanging period.\n");

        // When autoEvent signals the second time, dispose of 
        // the timer.
        autoEvent.WaitOne(5000, false);
        stateTimer.Dispose();
        Console.WriteLine("\nDestroying timer.");
    }
}

class StatusChecker
{
    int invokeCount, maxCount;

    public StatusChecker(int count)
    {
        invokeCount  = 0;
        maxCount = count;
    }

    // This method is called by the timer delegate.
    public void CheckStatus(Object stateInfo)
    {
        AutoResetEvent autoEvent = (AutoResetEvent)stateInfo;
        Console.WriteLine("{0} Checking status {1,2}.", 
            DateTime.Now.ToString("h:mm:ss.fff"), 
            (++invokeCount).ToString());

        if(invokeCount == maxCount)
        {
            // Reset the counter and signal Main.
            invokeCount  = 0;
            autoEvent.Set();
        }
    }
}

두 예제 모두 MSDN 페이지에서 가져 왔습니다.


답변

이를 위해 서비스를 사용하지 마십시오. 일반 응용 프로그램을 만들고이를 실행할 예약 된 작업을 만듭니다.

이것은 일반적으로 유지되는 모범 사례입니다. Jon Galloway는 저에게 동의합니다. 아니면 그 반대 일 수도 있습니다. 어느 쪽이든간에 타이머에서 실행되는 간헐적 작업을 수행하기 위해 Windows 서비스를 만드는 것은 모범 사례가 아닙니다.

“타이머를 실행하는 Windows 서비스를 작성하는 경우 솔루션을 다시 평가해야합니다.”

–Jon Galloway, ASP.NET MVC 커뮤니티 프로그램 관리자, 작성자, 파트 타임 슈퍼 히어로


답변

둘 중 하나가 정상적으로 작동합니다. 실제로 System.Threading.Timer는 System.Timers.Timer를 내부적으로 사용합니다.

그렇긴하지만 System.Timers.Timer를 오용하기 쉽습니다. Timer 객체를 변수의 어딘가에 저장하지 않으면 가비지 수집 될 수 있습니다. 이 경우 타이머가 더 이상 실행되지 않습니다. Dispose 메서드를 호출하여 타이머를 중지하거나 약간 더 멋진 래퍼 인 System.Threading.Timer 클래스를 사용합니다.

지금까지 어떤 문제를 보셨습니까?


답변

다른 접근 방식을 고려하는 것이 가장 좋은 이전 의견에 동의합니다. 내 제안은 콘솔 응용 프로그램을 작성하고 Windows 스케줄러를 사용하는 것입니다.

이것은 :

  • 스케줄러 동작을 복제하는 배관 코드 감소
  • 애플리케이션 코드에서 추상화 된 모든 스케줄링 로직으로 스케줄링 동작 (예 : 주말에만 실행) 측면에서 더 큰 유연성을 제공합니다.
  • 구성 파일 등에서 구성 값을 설정할 필요없이 매개 변수에 대한 명령 줄 인수를 활용합니다.
  • 개발 중 디버그 / 테스트가 훨씬 더 쉽습니다.
  • 지원 사용자가 콘솔 애플리케이션을 직접 호출하여 실행하도록 허용 (예 : 지원 상황에서 유용함)

답변

이미 언급했듯이 System.Threading.TimerSystem.Timers.Timer작동합니다. 둘 사이의 큰 차이점 System.Threading.Timer은 다른 하나를 감싸는 래퍼라는 것입니다.

System.Threading.TimerSystem.Timers.Timer모든 예외를 삼키는 동안 더 많은 예외 처리가 있습니다
.

이것은 과거에 저에게 큰 문제를 주었으므로 항상 ‘System.Threading.Timer’를 사용하고 여전히 예외를 매우 잘 처리합니다.


답변

이 스레드가 조금 오래되었지만 내가 가진 특정 시나리오에 유용하다는 것을 알고 System.Threading.Timer있으며 좋은 접근 방식이 될 수있는 또 다른 이유가 있음을 주목할 가치가 있다고 생각했습니다 . 시간이 오래 걸릴 수있는 작업을 주기적으로 실행해야하고 작업간에 전체 대기 기간이 사용되는지 확인하거나 이전 작업이 완료되기 전에 작업을 다시 실행하지 않으려는 경우 작업이 타이머 기간보다 오래 걸립니다. 다음을 사용할 수 있습니다.

using System;
using System.ServiceProcess;
using System.Threading;

    public partial class TimerExampleService : ServiceBase
    {
        private AutoResetEvent AutoEventInstance { get; set; }
        private StatusChecker StatusCheckerInstance { get; set; }
        private Timer StateTimer { get; set; }
        public int TimerInterval { get; set; }

        public CaseIndexingService()
        {
            InitializeComponent();
            TimerInterval = 300000;
        }

        protected override void OnStart(string[] args)
        {
            AutoEventInstance = new AutoResetEvent(false);
            StatusCheckerInstance = new StatusChecker();

            // Create the delegate that invokes methods for the timer.
            TimerCallback timerDelegate =
                new TimerCallback(StatusCheckerInstance.CheckStatus);

            // Create a timer that signals the delegate to invoke 
            // 1.CheckStatus immediately, 
            // 2.Wait until the job is finished,
            // 3.then wait 5 minutes before executing again. 
            // 4.Repeat from point 2.
            Console.WriteLine("{0} Creating timer.\n",
                DateTime.Now.ToString("h:mm:ss.fff"));
            //Start Immediately but don't run again.
            StateTimer = new Timer(timerDelegate, AutoEventInstance, 0, Timeout.Infinite);
            while (StateTimer != null)
            {
                //Wait until the job is done
                AutoEventInstance.WaitOne();
                //Wait for 5 minutes before starting the job again.
                StateTimer.Change(TimerInterval, Timeout.Infinite);
            }
            //If the Job somehow takes longer than 5 minutes to complete then it wont matter because we will always wait another 5 minutes before running again.
        }

        protected override void OnStop()
        {
            StateTimer.Dispose();
        }
    }

    class StatusChecker
        {

            public StatusChecker()
            {
            }

            // This method is called by the timer delegate.
            public void CheckStatus(Object stateInfo)
            {
                AutoResetEvent autoEvent = (AutoResetEvent)stateInfo;
                Console.WriteLine("{0} Start Checking status.",
                    DateTime.Now.ToString("h:mm:ss.fff"));
                //This job takes time to run. For example purposes, I put a delay in here.
                int milliseconds = 5000;
                Thread.Sleep(milliseconds);
                //Job is now done running and the timer can now be reset to wait for the next interval
                Console.WriteLine("{0} Done Checking status.",
                    DateTime.Now.ToString("h:mm:ss.fff"));
                autoEvent.Set();
            }
        }


답변