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


108

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

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

답변:


118

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 페이지에서 가져 왔습니다.


1
당신이 제안하는 이유 : GC.KeepAlive (aTimer) ;, aTimer는 바로 인스턴스 변수입니다. 예를 들어 그것이 폼의 인스턴스 변수라면, 폼이있는 한 항상 참조가있을 것입니다. 그렇지 않습니까?
giorgim

37

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

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

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

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


26
서비스가 하루 종일 실행되도록 의도 된 경우 예약 된 작업보다는 서비스가 의미가있을 수 있습니다. 또는 서비스가 있으면 애플리케이션 팀만큼 잘 모르는 인프라 그룹의 관리 및 로깅이 단순화 될 수 있습니다. 그러나 서비스가 필요하다는 가정에 의문을 제기하는 것은 전적으로 타당하며 이러한 부정적인 등급은 가치가 없습니다. 둘 다 +1.
sfuqua 2010

2
@mr 넌센스. 예약 된 작업은 OS의 핵심 부분입니다. FUD를 자신에게 맡기십시오.

4
@MR : 저는 전도자가 아니고 현실 주의자입니다. 그리고 현실은 저에게 예정된 작업이 "매우 버그가 많지 않은"것이 아니라는 것을 가르쳐주었습니다. 사실 그것을지지한다고 주장한 사람은 당신에게 달려 있습니다. 그렇지 않으면 당신이하는 일은 두려움, 불확실성, 의심을 퍼뜨리는 것뿐입니다.

13
나는 여기 수렁에 들어가는 것이 싫지만 MR을 조금 방어해야합니다. 회사에서 Windows 콘솔 앱인 몇 가지 중요한 애플리케이션을 실행하고 있습니다. 나는 그들 모두를 실행하기 위해 Windows 작업 스케줄러를 사용했습니다. 적어도 5 번 이상 스케줄러 서비스가 어떻게 든 "혼란스러워"하는 문제가있었습니다. 작업이 실행되지 않았고 일부는 이상한 상태였습니다. 유일한 해결책은 서버를 재부팅하거나 스케줄러 서비스를 중지했다가 시작하는 것입니다. 관리자 권한 없이는 할 수없고 프로덕션 환경에서는 허용되지 않습니다. 내 $ 0.02.
SpaceCowboy74

6
실제로 몇 대의 서버에서 발생했습니다 (지금까지 3 대). 그래도 그것이 표준이라고 말하지 않을 것입니다. 때로는 자신의 방법으로 무언가를 수행하는 것이 나쁘지 않다고 말합니다.
SpaceCowboy74 2013 년

7

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

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

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


Windows Phone 앱이 System.Threading.Timer에만 액세스 할 수있는 이유가 궁금합니다.
Stonetip

윈도우 폰은 아마도 프레임 워크의 더 가벼운 버전을 가지고있을 것입니다. 두 가지 방법을 모두 사용할 수있는 모든 추가 코드는 아마도 필요하지 않을 것이므로 포함되지 않습니다. 나는 Nick의 대답이 Windows 폰이 System.Timers.Timer예외를 처리하지 않기 때문에 액세스 권한이없는 이유를 더 잘 제공한다고 생각 합니다.
말라기

2

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

이것은 :

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

3
그러나 이것은 로그온 한 사용자가 필요합니까? 따라서 서비스가 서버에서 연중 무휴로 실행된다면 더 좋을 것입니다.
JP Hellemons

1

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

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

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


0

이 스레드가 조금 오래되었지만 내가 가진 특정 시나리오에 유용하다는 것을 알고 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();
            }
        }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.