C #에서 매일 특정 시간에 메서드를 호출하는 방법은 무엇입니까?


83

나는 SO를 검색하고 Quartz.net에 대한 답변을 찾았습니다. 하지만 내 프로젝트에는 너무 큰 것 같습니다. 동등한 솔루션을 원하지만 더 간단하고 코드 내에서 (외부 라이브러리가 필요하지 않음). 매일 특정 시간에 메소드를 호출하려면 어떻게해야합니까?

이에 대한 몇 가지 정보를 추가해야합니다.

  • 이 작업을 수행하는 가장 간단하고 추악한 방법은 매초 / 분마다 시간을 확인하고 적절한 시간에 메서드를 호출하는 것입니다.

나는 이것을하기위한보다 효과적인 방법을 원하고, 시간을 지속적으로 확인할 필요가 없으며, 작업이 완료되지 않았는지 여부를 제어 할 수 있습니다. 방법이 실패하면 (문제로 인해), 프로그램은 로그 쓰기 / 이메일 보내기를 알아야합니다. 그래서 작업을 예약하는 것이 아니라 메서드를 호출해야합니다.

이 솔루션을 찾았 습니다 Java 의 Java 에서 고정 시간에 메서드 호출 . C #에도 비슷한 방법이 있습니까?

편집 : 나는 이것을했다. void Main ()에 매개 변수를 추가하고이 매개 변수로 프로그램을 실행하기 위해 bat (Windows 작업 스케줄러에 의해 예약 됨)를 만들었습니다. 프로그램이 실행되고 작업을 수행 한 다음 종료됩니다. 작업이 실패하면 로그를 작성하고 이메일을 보낼 수 있습니다. 이 접근 방식은 내 요구 사항에 잘 맞습니다. :)


2
연결된 질문은 실행중인 앱 내의 메서드를 주기적으로 호출해야 함을 나타내는 것 같습니다. 그럴까요? 프로세스 내 스케줄링이 필요한지 또는 Windows 스케줄러 만 사용할 수 있는지 여부에 영향을줍니다.
— paxdiablo

내 프로그램은 요구 사항으로 지속적으로 실행됩니다.
— Quan Mai

이봐 요, 당신이 내 대답을 "못생긴"이라고 부르 다니 믿을 수가 없어요. 그들의 싸우는 '단어 :-)
— paxdiablo

귀하의 답변에 대해 의미하지 않았습니다. 나도 그것에 대해 생각했고 나도 못 생겼다.
— Quan Mai

답변:


86
  • 원하는 작업을 수행하는 콘솔 앱 만들기
  • Windows " 예약 된 작업 "기능을 사용하여 실행에 필요한 시간에 해당 콘솔 앱을 실행하도록합니다.

그게 정말 필요한 전부입니다!

업데이트 : 앱 내에서이 작업을 수행하려면 몇 가지 옵션이 있습니다.

  • A의 윈도우 폼의 응용 프로그램, 당신은 활용할 수있는 Application.Idle이벤트와 당신의 메소드를 호출 할 수있는 일의 시간에 도달했는지 확인하십시오. 이 메서드는 앱이 다른 작업으로 바쁘지 않을 때만 호출됩니다. 목표 시간에 도달했는지 확인하는 빠른 확인은 앱에 너무 많은 스트레스를주지 않아야한다고 생각합니다.
  • ASP.NET 웹앱에는 예약 된 이벤트를 "시뮬레이션"하는 방법이 있습니다.이 CodeProject 기사를 확인 하십시오.
  • 물론 모든 .NET 앱에서 "자신 만의 롤링"을 수행 할 수도 있습니다 . 샘플 구현은 이 CodeProject 기사 를 참조하십시오.

업데이트 # 2 : 60 분마다 확인하려면 60 분마다 깨어나는 타이머를 만들고 시간이 다되면 메서드를 호출합니다.

이 같은:

using System.Timers;

const double interval60Minutes = 60 * 60 * 1000; // milliseconds to one hour

Timer checkForTime = new Timer(interval60Minutes);
checkForTime.Elapsed += new ElapsedEventHandler(checkForTime_Elapsed);
checkForTime.Enabled = true;

그런 다음 이벤트 핸들러에서 :

void checkForTime_Elapsed(object sender, ElapsedEventArgs e)
{
    if (timeIsReady())
    {
       SendEmail();
    }
}

전에 그것에 대해 생각했습니다. :). 그러나 :) 내 프로그램이 계속 실행되고있는 경우 나, 다른 방법을 알고 싶어요
— 콴 마이에게

Winform 앱입니다. 나는 그것이 설계 변경 내 상사를 설득하려고합니다,하지만 처음에 나는 그의 요구를 채우기 위해 시도해야합니다 : P
— 콴 마이

4
뭐라고합니까 timeIsReady()호출합니까?
— brimble2010

1
@ brimble2010 : 원하는대로 할 수 있습니다. 예를 들어 일시적으로 "차단 된"타임 슬롯이있는 테이블 (예 : 아침 3, 4, 5시에 실행하지 않음)을 가질 수 있습니다. 추가 확인, 60 분마다 시작되는 것 외에도 ....
— marc_s

1
Windows 10 및 .Net 4.7.2에서 오늘까지 훌륭하게 작동하는 매우 훌륭하고 간단하며 짧고 깨끗한 솔루션입니다. !! 감사합니다.
— MinimalTech

20

사용하기 쉽고 외부 라이브러리를 사용할 필요가없는 간단한 스케줄러를 만들었습니다. TaskScheduler는 타이머에 대한 참조를 유지하는 싱글 톤이므로 타이머가 가비지 수집되지 않고 여러 작업을 예약 할 수 있습니다. 스케줄링시이 시간이 초과되면 첫 번째 실행 (시간 및 분)을 설정할 수 있습니다.이 시간이 다음날 스케줄링 시작됩니다. 그러나 코드를 사용자 지정하는 것은 쉽습니다.

새 작업을 예약하는 것은 매우 간단합니다. 예 : 11:52에 첫 번째 작업은 15 초마다, 두 번째 예는 5 초마다입니다. 매일 실행하려면 24를 3 매개 변수로 설정하십시오.

TaskScheduler.Instance.ScheduleTask(11, 52, 0.00417, 
    () => 
    {
        Debug.WriteLine("task1: " + DateTime.Now);
        //here write the code that you want to schedule
    });

TaskScheduler.Instance.ScheduleTask(11, 52, 0.00139,
    () =>
    {
        Debug.WriteLine("task2: " + DateTime.Now);
        //here write the code that you want to schedule
    });

내 디버그 창 :

task2: 07.06.2017 11:52:00
task1: 07.06.2017 11:52:00
task2: 07.06.2017 11:52:05
task2: 07.06.2017 11:52:10
task1: 07.06.2017 11:52:15
task2: 07.06.2017 11:52:15
task2: 07.06.2017 11:52:20
task2: 07.06.2017 11:52:25
...

프로젝트에이 클래스를 추가하기 만하면됩니다.

public class TaskScheduler
{
    private static TaskScheduler _instance;
    private List<Timer> timers = new List<Timer>();

    private TaskScheduler() { }

    public static TaskScheduler Instance => _instance ?? (_instance = new TaskScheduler());

    public void ScheduleTask(int hour, int min, double intervalInHour, Action task)
    {
        DateTime now = DateTime.Now;
        DateTime firstRun = new DateTime(now.Year, now.Month, now.Day, hour, min, 0, 0);
        if (now > firstRun)
        {
            firstRun = firstRun.AddDays(1);
        }

        TimeSpan timeToGo = firstRun - now;
        if (timeToGo <= TimeSpan.Zero)
        {
            timeToGo = TimeSpan.Zero;
        }

        var timer = new Timer(x =>
        {
            task.Invoke();
        }, null, timeToGo, TimeSpan.FromHours(intervalInHour));

        timers.Add(timer);
    }
}

new Timer에는 4 개의 인수를 사용하는 메서드가 없습니다.
— Fandango68

여기 Timer가 System.Timers에서 나온 것이라고 가정합니다. 샘플 작업 프로그램을 제공 할 수 있습니까? 감사합니다
— Fandango68

1
@ Fandango68 System.Threading 네임 스페이스에서 Timer를 사용했습니다. System.Timers에 또 다른 타이머가 있습니다. 나는 당신이 위의 거짓 사용을 사용했다고 생각합니다.
— jannagy02 2018

@ jannagy02 월요일과 같은 특정 날짜에 작업을 예약하는 방법은 무엇입니까?
— Sruthi Varghese

유용합니다. 감사합니다.
— james


8

다른 사람들이 말했듯이 콘솔 앱을 사용하여 예정된 시간에 실행할 수 있습니다. 다른 사람들이 말하지 않은 것은이 앱이 메인 ​​애플리케이션에서 기다리고있는 크로스 프로세스 EventWaitHandle을 트리거 할 수 있다는 것입니다.

콘솔 앱 :

class Program
{
    static void Main(string[] args)
    {
        EventWaitHandle handle = 
            new EventWaitHandle(true, EventResetMode.ManualReset, "GoodMutexName");
        handle.Set();
    }
}

주요 앱 :

private void Form1_Load(object sender, EventArgs e)
{
    // Background thread, will die with application
    ThreadPool.QueueUserWorkItem((dumby) => EmailWait());
}

private void EmailWait()
{
    EventWaitHandle handle = 
        new EventWaitHandle(false, EventResetMode.ManualReset, "GoodMutexName");

    while (true)
    {
        handle.WaitOne();

        SendEmail();

        handle.Reset();
    }
}

4

내가 알고있는 가장 간단한 방법은 Windows 작업 스케줄러를 사용하여 특정 시간에 코드를 실행하거나 응용 프로그램을 영구적으로 실행하고 특정 시간을 확인하거나 작업을 수행하는 Windows 서비스를 작성하는 것입니다. 같은.


4

나는 이것이 오래되었다는 것을 알고 있지만 이것은 어떻습니까?

다음 실행 시간까지의 시간을 계산하는 시작시 실행할 타이머를 빌드하십시오. 런타임을 처음 호출 할 때 첫 번째 타이머를 취소하고 새로운 일일 타이머를 시작합니다. 매일 또는 원하는 주기로 변경하십시오.


9
그리고 일광 절약 시간이 변경되는 동안 실패하는 것을보십시오. . .
— Jim Mischel 2013 년

3

다음은 TPL을 사용하여이를 수행하는 방법입니다. 타이머 등을 생성 / 삭제할 필요가 없습니다.

void ScheduleSomething()
{

    var runAt = DateTime.Today + TimeSpan.FromHours(16);

    if (runAt <= DateTime.Now)
    {
        DoSomething();
    }
    else
    {
        var delay = runAt - DateTime.Now;
        System.Threading.Tasks.Task.Delay(delay).ContinueWith(_ => DoSomething());
    }

}

void DoSomething()
{
    // do somethig
}

2

이 작은 프로그램이 해결책이되어야합니다 ;-)

이것이 모두에게 도움이되기를 바랍니다.

using System;
using System.Threading;
using System.Threading.Tasks;

namespace DailyWorker
{
    class Program
    {
        static void Main(string[] args)
        {
            var cancellationSource = new CancellationTokenSource();

            var utils = new Utils();
            var task = Task.Run(
                () => utils.DailyWorker(12, 30, 00, () => DoWork(cancellationSource.Token), cancellationSource.Token));

            Console.WriteLine("Hit [return] to close!");
            Console.ReadLine();

            cancellationSource.Cancel();
            task.Wait();
        }

        private static void DoWork(CancellationToken token)
        {
            while (!token.IsCancellationRequested)
            {
                Console.Write(DateTime.Now.ToString("G"));
                Console.CursorLeft = 0;
                Task.Delay(1000).Wait();
            }
        }
    }

    public class Utils
    {
        public void DailyWorker(int hour, int min, int sec, Action someWork, CancellationToken token)
        {
            while (!token.IsCancellationRequested)
            {
                var dateTimeNow = DateTime.Now;
                var scanDateTime = new DateTime(
                    dateTimeNow.Year,
                    dateTimeNow.Month,
                    dateTimeNow.Day,
                    hour,       // <-- Hour when the method should be started.
                    min,  // <-- Minutes when the method should be started.
                    sec); // <-- Seconds when the method should be started.

                TimeSpan ts;
                if (scanDateTime > dateTimeNow)
                {
                    ts = scanDateTime - dateTimeNow;
                }
                else
                {
                    scanDateTime = scanDateTime.AddDays(1);
                    ts           = scanDateTime - dateTimeNow;
                }

                try
                {
                     Task.Delay(ts).Wait(token);
                }
                catch (OperationCanceledException)
                {
                    break;
                }

                // Method to start
                someWork();
            }
        }
    }
}

1

실행 파일을 실행하려면 Windows 예약 된 작업을 사용하십시오. 나는 당신이 현재 프로그램에서 실행될 메소드를 원한다고 가정 할 것이다.

메서드가 호출 된 마지막 날짜를 저장하는 스레드를 계속 실행하지 않는 이유는 무엇입니까?

예를 들어 1 분마다 깨우고 현재 시간이 지정된 시간보다 크고 저장된 마지막 날짜가 현재 날짜가 아닌 경우 메서드를 호출 한 다음 날짜를 업데이트합니다.


1

나일 수도 있지만 대부분의 답변이 완전하지 않거나 제대로 작동하지 않는 것 같습니다. 나는 아주 빠르고 더러운 것을 만들었습니다. 이런 식으로하는 것이 얼마나 좋은 아이디어인지 확실하지 않지만 매번 완벽하게 작동합니다.

while (true)
{
    if(DateTime.Now.ToString("HH:mm") == "22:00")
    {
        //do something here
        //ExecuteFunctionTask();
        //Make sure it doesn't execute twice by pausing 61 seconds. So that the time is past 2200 to 2201
        Thread.Sleep(61000);
    }

    Thread.Sleep(10000);
}

1
내가 볼 수있는 한 이것은 바쁜 대기이므로 좋은 생각이 아닙니다. en.wikipedia.org/wiki/Busy_waiting
— 토미 Ivarsson이

1

최근에 매일 다시 시작해야하는 ac # 앱을 작성했습니다. 이 질문이 오래되었다는 것을 알고 있지만 다른 가능한 해결책을 추가하는 것이 아프다고 생각하지 않습니다. 이것이 내가 지정된 시간에 매일 다시 시작하는 방법입니다.

public void RestartApp()
{
  AppRestart = AppRestart.AddHours(5);
  AppRestart = AppRestart.AddMinutes(30);
  DateTime current = DateTime.Now;
  if (current > AppRestart) { AppRestart = AppRestart.AddDays(1); }

  TimeSpan UntilRestart = AppRestart - current;
  int MSUntilRestart = Convert.ToInt32(UntilRestart.TotalMilliseconds);

  tmrRestart.Interval = MSUntilRestart;
  tmrRestart.Elapsed += tmrRestart_Elapsed;
  tmrRestart.Start();
}

타이머가 범위 내에 있는지 확인하려면 메서드를 사용하여 System.Timers.Timer tmrRestart = new System.Timers.Timer()메서드 외부에서 만드는 것이 좋습니다 . RestartApp()양식로드 이벤트에 메소드 를 넣으십시오 . 응용 프로그램이 시작되면 AppRestartif 값 current이 다시 시작 시간보다 크면 1 일을 추가합니다.AppRestart 발생하고 타이머에 음수 값을 넣는 예외가 발생하지 않도록합니다. 에서 tmrRestart_Elapsed어떤 코드 이벤트 실행하면 특정 시간에 실행 된이 필요합니다. 응용 프로그램이 자체적으로 다시 시작되는 경우 반드시 타이머를 중지 할 필요는 없지만 문제가되지는 않습니다. 응용 프로그램이 다시 시작되지 않으면 RestartApp()메서드를 다시 호출 하면됩니다.


1

나는 이것이 매우 유용하다는 것을 알았습니다.

using System;
using System.Timers;

namespace ScheduleTimer
{
    class Program
    {
        static Timer timer;

        static void Main(string[] args)
        {
            schedule_Timer();
            Console.ReadLine();
        }

        static void schedule_Timer()
        {
            Console.WriteLine("### Timer Started ###");

            DateTime nowTime = DateTime.Now;
            DateTime scheduledTime = new DateTime(nowTime.Year, nowTime.Month, nowTime.Day, 8, 42, 0, 0); //Specify your scheduled time HH,MM,SS [8am and 42 minutes]
            if (nowTime > scheduledTime)
            {
                scheduledTime = scheduledTime.AddDays(1);
            }

            double tickTime = (double)(scheduledTime - DateTime.Now).TotalMilliseconds;
            timer = new Timer(tickTime);
            timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
            timer.Start();
        }

        static void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            Console.WriteLine("### Timer Stopped ### \n");
            timer.Stop();
            Console.WriteLine("### Scheduled Task Started ### \n\n");
            Console.WriteLine("Hello World!!! - Performing scheduled task\n");
            Console.WriteLine("### Task Finished ### \n\n");
            schedule_Timer();
        }
    }
}


0

매 60 분마다 실행할 시간을 설정하는 대신 남은 시간을 계산하고 타이머를 절반 (또는 다른 일부)으로 설정할 수 있습니다. 이렇게하면 시간을 많이 확인하지 않고 타이머 간격이 목표 시간에 가까워 질수록 정확도를 유지합니다.

예를 들어 지금부터 60 분 동안 무언가를하려는 경우 타이머 간격은 거의 다음과 같습니다.

30:00:00, 15:00:00, 07:30:00, 03:45:00, ..., 00:00:01, RUN!

아래 코드를 사용하여 하루에 한 번 서비스를 자동으로 다시 시작합니다. 나는 타이머가 오랜 기간 동안 신뢰할 수 없다는 것을 발견했기 때문에 스레드를 사용하지만,이 예제에서는 더 많은 비용이 들지만이 목적을 위해 만들어진 유일한 것이므로 중요하지 않습니다.

(VB.NET에서 변환)

autoRestartThread = new System.Threading.Thread(autoRestartThreadRun);
autoRestartThread.Start();

...

private void autoRestartThreadRun()
{
    try {
        DateTime nextRestart = DateAndTime.Today.Add(CurrentSettings.AutoRestartTime);
        if (nextRestart < DateAndTime.Now) {
            nextRestart = nextRestart.AddDays(1);
        }

        while (true) {
            if (nextRestart < DateAndTime.Now) {
                LogInfo("Auto Restarting Service");
                Process p = new Process();
                p.StartInfo.FileName = "cmd.exe";
                p.StartInfo.Arguments = string.Format("/C net stop {0} && net start {0}", "\"My Service Name\"");
                p.StartInfo.LoadUserProfile = false;
                p.StartInfo.UseShellExecute = false;
                p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                p.StartInfo.CreateNoWindow = true;
                p.Start();
            } else {
                dynamic sleepMs = Convert.ToInt32(Math.Max(1000, nextRestart.Subtract(DateAndTime.Now).TotalMilliseconds / 2));
                System.Threading.Thread.Sleep(sleepMs);
            }
        }
    } catch (ThreadAbortException taex) {
    } catch (Exception ex) {
        LogError(ex);
    }
}

참고 최소 간격을 1000ms로 설정했습니다. 필요한 정확도에 따라이 간격을 늘리거나 줄이거 나 제거 할 수 있습니다.

또한 응용 프로그램이 닫힐 때 스레드 / 타이머를 중지해야합니다.


0

이에 대한 간단한 접근 방식이 있습니다. 이렇게하면 작업이 발생하기 전에 1 분 지연됩니다. Thread.Sleep (); 만들기 위해 초를 추가 할 수도 있습니다. 짧아.

private void DoSomething(int aHour, int aMinute)
{
    bool running = true;
    while (running)
    {
        Thread.Sleep(1);
        if (DateTime.Now.Hour == aHour && DateTime.Now.Minute == aMinute)
        {
            Thread.Sleep(60 * 1000); //Wait a minute to make the if-statement false
            //Do Stuff
        }
    }
}

0

24 시간

var DailyTime = "16:59:00";
            var timeParts = DailyTime.Split(new char[1] { ':' });

            var dateNow = DateTime.Now;
            var date = new DateTime(dateNow.Year, dateNow.Month, dateNow.Day,
                       int.Parse(timeParts[0]), int.Parse(timeParts[1]), int.Parse(timeParts[2]));
            TimeSpan ts;
            if (date > dateNow)
                ts = date - dateNow;
            else
            {
                date = date.AddDays(1);
                ts = date - dateNow;
            }

            //waits certan time and run the code
            Task.Delay(ts).ContinueWith((x) => OnTimer());

public void OnTimer()
    {
        ViewBag.ErrorMessage = "EROOROOROROOROR";
    }

0

하나의 작업에 대한 간단한 예 :

using System;
using System.Timers;

namespace ConsoleApp
{
    internal class Scheduler
    {
        private static readonly DateTime scheduledTime = 
            new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 10, 0, 0);
        private static DateTime dateTimeLastRunTask;

        internal static void CheckScheduledTask()
        {
            if (dateTimeLastRunTask.Date < DateTime.Today && scheduledTime.TimeOfDay < DateTime.Now.TimeOfDay)
            {
                Console.WriteLine("Time to run task");
                dateTimeLastRunTask = DateTime.Now;
            }
            else
            {
                Console.WriteLine("not yet time");
            }
        }
    }

    internal class Program
    {
        private static Timer timer;

        static void Main(string[] args)
        {
            timer = new Timer(5000);
            timer.Elapsed += OnTimer;
            timer.Start();
            Console.ReadLine();
        }

        private static void OnTimer(object source, ElapsedEventArgs e)
        {
            Scheduler.CheckScheduledTask();
        }
    }
}

이것은 타이머처럼 보이지 않고 정확하지 않은 간격처럼 보입니다.
— james

타이머 시작 시간은 scheduleTime 변수로 설정됩니다. Console.WriteLine ( "Time to run task") 메서드는이 시간에 매일 실행됩니다.
— ikleds

설명 해주셔서 감사합니다.
— james

아니에요!
— ikleds

-1

System.Threading.Timer를 사용한 솔루션 :

    private void nameOfMethod()
    {
        //do something
    }

    /// <summary>
    /// run method at 22:00 every day
    /// </summary>
    private void runMethodEveryDay()
    {
        var runAt = DateTime.Today + TimeSpan.FromHours(22);

        if(runAt.Hour>=22)
            runAt = runAt.AddDays(1.00d); //if aplication is started after 22:00 

        var dueTime = runAt - DateTime.Now; //time before first run ; 

        long broj3 = (long)dueTime.TotalMilliseconds;
        TimeSpan ts2 = new TimeSpan(24, 0, 1);//period of repeating method
        long broj4 = (long)ts2.TotalMilliseconds;
        timer2 = new System.Threading.Timer(_ => nameOfMethod(), null, broj3, broj4);
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.