매일 작업을 수행하도록 C # Windows 서비스를 예약하려면 어떻게해야합니까?


84

C # (. NET 1.1)으로 작성된 서비스가 있고 매일 밤 자정에 정리 작업을 수행하기를 원합니다. 모든 코드를 서비스 내에 포함시켜야하는데이를 수행하는 가장 쉬운 방법은 무엇입니까? 사용 Thread.Sleep()및 롤오버 시간 확인?


10
하루 종일 기억에 남고 자정에 무언가를하는 .NET 프로세스를 정말로 만들고 싶습니까? 도구를 설치할 때 앱을 시작하는 자정 (또는 다른 구성 가능한 시간)에 실행되는 시스템 이벤트를 설정할 수없는 이유는 무엇입니까?
Robert P

6
20 ~ 40 메가의 메모리를 소비하는 관리 형 서비스가 하루에 한 번을 제외하고는 아무 일도하지 않고 항상 실행된다는 사실을 알게되면 약간 화가 날 것입니다. :)
Robert P

그 중복 된 링크
CSA

답변:


86

Thread.Sleep ()을 사용하지 않을 것입니다. 다른 사람들이 언급했듯이 예약 된 작업을 사용하거나 서비스 내부에 타이머를 설정하여 주기적으로 (예 : 10 분마다) 실행하고 마지막 실행 이후 날짜가 변경되었는지 확인합니다.

private Timer _timer;
private DateTime _lastRun = DateTime.Now.AddDays(-1);

protected override void OnStart(string[] args)
{
    _timer = new Timer(10 * 60 * 1000); // every 10 minutes
    _timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
    _timer.Start();
    //...
}


private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    // ignore the time, just compare the date
    if (_lastRun.Date < DateTime.Now.Date)
    {
        // stop the timer while we are running the cleanup task
        _timer.Stop();
        //
        // do cleanup stuff
        //
        _lastRun = DateTime.Now;
        _timer.Start();
    }
}

28
시간을 확인하기 위해 1 분마다 깨어나지 않고 자정에 만료되도록 타이머를 설정하는 것이 더 합리적이지 않을까요?
Kibbee

11
@Kibbee : 어떤 이유로 든 자정에 서비스가 실행되지 않는 경우 서비스가 다시 실행되는 즉시 작업을 실행하고 싶을 수 있습니다.
M4N

4
그런 다음 서비스 시작시 코드를 추가하여 서비스가 실행 중이 아니 었으므로 즉시 실행해야하는지 확인해야합니다. 10 분마다 계속 확인해서는 안됩니다. 시작할 때 실행해야하는지 확인한 다음 다음에 실행할 시간을 파악하지 못하는 경우. 그 후에 필요한만큼 타이머를 설정합니다.
Kibbee

3
@Kibbee : 네 동의합니다 (우리가 논의중인 사소한 세부 사항입니다). 그러나 타이머가 10 초마다 실행 되더라도 아무런 해를 끼치 지 않습니다 (즉, 시간이 많이 걸리지 않음).
M4N

1
좋은 해결책이지만 위의 코드에는 _timer.start ()가 누락되었으며 _lastRun은 다음과 같이 어제로 설정되어야합니다. private DateTime _lastRun = DateTime.Now.AddDays (-1);
Zulu Z

72

Quartz.NET을 확인하십시오 . Windows 서비스 내에서 사용할 수 있습니다. 구성된 일정에 따라 작업을 실행할 수 있으며 간단한 "cron 작업"구문도 지원합니다. 나는 그것으로 많은 성공을 거두었습니다.

다음은 사용법에 대한 간단한 예입니다.

// Instantiate the Quartz.NET scheduler
var schedulerFactory = new StdSchedulerFactory();
var scheduler = schedulerFactory.GetScheduler();

// Instantiate the JobDetail object passing in the type of your
// custom job class. Your class merely needs to implement a simple
// interface with a single method called "Execute".
var job = new JobDetail("job1", "group1", typeof(MyJobClass));

// Instantiate a trigger using the basic cron syntax.
// This tells it to run at 1AM every Monday - Friday.
var trigger = new CronTrigger(
    "trigger1", "group1", "job1", "group1", "0 0 1 ? * MON-FRI");

// Add the job to the scheduler
scheduler.AddJob(job, true);
scheduler.ScheduleJob(trigger);

2
좋은 제안-기본 타이머보다 약간 더 복잡한 작업을 수행하자마자 Quartz를보아야합니다.
serg10의

2
쿼츠는 사용하기 매우 쉽습니다. 아주 간단한 작업이라도 계속해서 사용하십시오. 일정을 쉽게 조정할 수있는 기능을 제공합니다.
Andy White

정말 간단하지 않습니까? 대부분의 초보자는 여기에서이 문제를 잡을 것입니다. stackoverflow.com/questions/24628372/…
Emil

21

일상적인 일? 예약 된 작업 (제어판)이어야하는 것 같습니다. 여기서 서비스가 필요하지 않습니다.


15

실제 서비스 여야합니까? Windows 제어판에서 내장 된 예약 된 작업을 사용할 수 있습니다.


서비스가 이미 존재하므로 여기에 기능을 추가하는 것이 더 쉬울 수 있습니다.
M4N

1
이와 같은 좁은 목적의 서비스를 콘솔 앱으로 변환하는 것은 간단해야합니다.
Stephen Martin

7
그는 이미 "모든 코드를 서비스 내에 포함시켜야합니다"라고 말했습니다. 원래 요구 사항에 의문을 제기 할 필요가 없다고 생각합니다. 상황에 따라 다르지만 때로는 예약 된 작업을 통해 서비스를 사용하는 데 매우 좋은 이유가 있습니다.
jeremcc 2009

3
+1 : 항상 모든면에서 문제를 볼 필요가 있기 때문입니다.
GvS 2009

6
실제로 하루에 하나의 작업 만 실행했다면 예약 된 작업으로 실행되는 간단한 콘솔 앱이 좋을 것 같습니다. 내 요점은 모든 것이 상황에 따라 다르며 "Windows 서비스를 사용하지 마십시오"라는 말은 IMO에 유용한 대답이 아니라는 것입니다.
jeremcc 2009

3

이것을 달성하는 방법은 타이머를 사용하는 것입니다.

서버 타이머를 실행하고 60 초마다시 / 분을 확인하도록합니다.

올바른시 / 분이면 프로세스를 실행하십시오.

나는 실제로 이것을 OnceADayRunner라고 부르는 기본 클래스로 추상화했습니다.

코드를 정리하고 여기에 게시하겠습니다.

    private void OnceADayRunnerTimer_Elapsed(object sender, ElapsedEventArgs e)
    {
        using (NDC.Push(GetType().Name))
        {
            try
            {
                log.DebugFormat("Checking if it's time to process at: {0}", e.SignalTime);
                log.DebugFormat("IsTestMode: {0}", IsTestMode);

                if ((e.SignalTime.Minute == MinuteToCheck && e.SignalTime.Hour == HourToCheck) || IsTestMode)
                {
                    log.InfoFormat("Processing at: Hour = {0} - Minute = {1}", e.SignalTime.Hour, e.SignalTime.Minute);
                    OnceADayTimer.Enabled = false;
                    OnceADayMethod();
                    OnceADayTimer.Enabled = true;

                    IsTestMode = false;
                }
                else
                {
                    log.DebugFormat("Not correct time at: Hour = {0} - Minute = {1}", e.SignalTime.Hour, e.SignalTime.Minute);
                }
            }
            catch (Exception ex)
            {
                OnceADayTimer.Enabled = true;
                log.Error(ex.ToString());
            }

            OnceADayTimer.Start();
        }
    }

메서드의 핵심은 e.SignalTime.Minute / Hour 검사에 있습니다.

테스트 등을위한 후크가 있지만 이것이 모든 작업을 수행하기 위해 경과 된 타이머의 모습입니다.


바쁜 서버로 인해 약간의 지연이 발생하면 시간 + 분을 놓칠 수 있습니다.
Jon B

1
진실. 이 작업을 수행 할 때 확인했습니다. 현재 시간이 00:00보다 큽니까? 그렇다면 마지막으로 작업을 실행 한 지 24 시간이 지났습니까? 그렇다면 작업을 실행하고 그렇지 않으면 다시 기다리십시오.
ZombieSheep

2

다른 사람들이 이미 작성했듯이 타이머는 설명한 시나리오에서 가장 좋은 옵션입니다.

정확한 요구 사항에 따라 매분 현재 시간을 확인할 필요가 없을 수도 있습니다. 정확히 자정에 작업을 수행 할 필요는 없지만 자정 이후 1 시간 이내에 마틴의 접근 방식 을 사용할 수 있습니다. 날짜가 변경되었는지 확인하는 .

자정에 작업을 수행하려는 이유가 컴퓨터의 작업 부하가 낮을 것으로 예상하기 때문이라면주의하십시오. 다른 사람들이 동일한 가정을하는 경우가 많고 갑자기 0:00에서 0 사이에 100 개의 정리 작업이 시작됩니다. 오전 1시

이 경우 다른 시간에 정리를 시작하는 것을 고려해야합니다. 나는 보통 시계 시간이 아니라 30 분에 그런 일을한다.


1

타이머를 사용하는 것이 좋지만 분이 아닌 45 초마다 확인하도록 설정하는 것이 좋습니다. 그렇지 않으면 타이머가 트리거되는 시간과 코드가 실행되고 현재 시간을 확인하는 시간 사이에 목표 분을 놓쳤을 수 있기 때문에 부하가 높으면 특정 분에 대한 확인이 누락되는 상황이 발생할 수 있습니다.


이미 한 번 실행되지 않았는지 확인하지 않는 경우 45 초마다 확인하는 경우 00:00을 두 번 누를 가능성이 있습니다.
Michael Meadows

좋은 지적. 이 문제는 확인 절차가 끝날 때 Sleep (15000)을 호출하여 피할 수 있습니다. (또는 아마도 16000ms, 안전을 위해 ;-)
Treb

동의합니다. 선택한 타이머 시간에 관계없이 이미 실행되었는지 확인해야합니다.
GWLlosa 2009


0

위의 솔루션이 작동하지 않는다는 것을 알게 된 사람들 this의 경우 클래스 내부에 오류 메시지가 말했듯이 비 제네릭 정적 클래스에서만 의미가있는 확장 메서드를 의미 할 수 있기 때문 입니다. 수업은 정적이 아닙니다. 이것은 문제의 인스턴스에서 작동하기 때문에 확장 메서드로서 의미가없는 것 같으므로 this.


-2

이 시도:

public partial class Service : ServiceBase
{
    private Timer timer;
    public Service()
    {
        InitializeComponent();
    }

    protected override void OnStart(string[] args)
    {
        SetTimer();
    }

    private void SetTimer()
    {
        if (timer == null)
        {
            timer = new Timer();
            timer.AutoReset = true;
            timer.Interval = 60000 * Convert.ToDouble(ConfigurationManager.AppSettings["IntervalMinutes"]);
            timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
            timer.Start();
        }
    }

    private void timer_Elapsed(object source, System.Timers.ElapsedEventArgs e)
    {
        //Do some thing logic here
    }

    protected override void OnStop()
    {
        // disposed all service objects
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.