C#で毎日特定の時間にメソッドを呼び出す方法は?


83

SOで検索し、Quartz.netに関する回答を見つけました。しかし、それは私のプロジェクトには大きすぎるようです。同等のソリューションが必要ですが、よりシンプルで(せいぜい)インコードです(外部ライブラリは必要ありません)。毎日、特定の時間にメソッドを呼び出すにはどうすればよいですか?

これに関するいくつかの情報を追加する必要があります:

  • これを行う最も簡単な(そして醜い)方法は、毎秒/分ごとに時間をチェックし、適切なタイミングでメソッドを呼び出すことです。

これを行うためのより効果的な方法が必要であり、時間を常にチェックする必要はなく、ジョブが実行されるかどうかを制御できます。メソッドが失敗した場合(問題が原因で)、プログラムはログに書き込む/電子メールを送信することを知っている必要があります。そのため、ジョブをスケジュールするのではなく、メソッドを呼び出す必要があります。

私はこの解決策を見つけましたJavaのJavaで決まった時間にメソッドを呼び出します。C#にも同様の方法がありますか?

編集:私はこれをしました。void Main()にパラメーターを追加し、このパラメーターを使用してプログラムを実行するためのバット(Windowsタスクスケジューラによってスケジュールされた)を作成しました。プログラムが実行され、ジョブが実行されてから終了します。ジョブが失敗した場合、ログを書き込んだり、メールを送信したりできます。このアプローチは私の要件によく合います:)


2
そのリンクされた質問は、実行中のアプリ内のメソッドを定期的に呼び出す必要があることを示しているようです。そうですか?これは、インプロセススケジューリングが必要かどうか、またはWindowsスケジューラを使用できるかどうかに影響します。
paxdiablo 2010

私のプログラムは、要件として、継続的に実行されます
Quan Mai

ねえ、あなたが私の答えを「醜い」と呼んだなんて信じられない。彼らの戦いの言葉:
paxdiablo 2010

あなたの答えについては意味しませんでした:p。私も考えてみたら醜いです。
Quan Mai

回答:


85
  • 探していることを実行するコンソールアプリを作成します
  • Windowsの「スケジュールされたタスク」機能を使用して、実行に必要なときにそのコンソールアプリを実行します。

本当に必要なのはそれだけです!

更新:アプリ内でこれを実行する場合は、いくつかのオプションがあります。

  • 、Windowsフォームアプリ、あなたが入り込む可能性がApplication.Idleイベントや、あなたのメソッドを呼び出すために一日の時間に達したかどうかを確認します。このメソッドは、アプリが他のもので忙しくないときにのみ呼び出されます。目標時間が達成されたかどうかを簡単に確認しても、アプリに過度のストレスがかかることはないと思います...
  • ASP.NET Webアプリには、スケジュールされたイベントの送信を「シミュレート」する方法があります。この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();
    }
}

以前にそれについて考えました。:)。しかし、私のプログラムは継続的に実行されます。もしあれば、別の方法を知りたいです:)
Quan Mai

これはWinformアプリです。上司にデザインを変更するよう説得しようとしますが、最初は彼の要件を満たすように努める必要があります:p
Quan Mai

4
timeIsReady()呼び出しは何をしますか?
brimble2010 2015

1
@ brimble2010:好きなことを何でもできます。たとえば、一時的に「ブロックされた」タイムスロット(たとえば、午前3、4、または5時に実行しない)などを含むテーブルを作成できます。これは完全にあなた次第です。60分ごとに起動されることに加えて、追加のチェック....
marc_s 2015

1
非常に素晴らしく、シンプルで、短く、クリーンなソリューションで、今日までWindows10と.Net4.7.2で優れた機能を発揮します。ありがとうございました。
minimalTech

19

使いやすく、外部ライブラリを使用する必要がないシンプルなスケジューラを作成しました。TaskSchedulerは、タイマーの参照を保持するシングルトンであるため、タイマーがガベージコレクションされることはなく、複数のタスクをスケジュールできます。この時刻のスケジュールが超過している場合は、最初の実行(時間と分)を設定して、その時刻の翌日に開始することができます。しかし、コードをカスタマイズするのは簡単です。

新しいタスクのスケジュールはとても簡単です。例:11:52では、最初のタスクは15秒ごとで、2番目の例は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

ここのタイマーはSystem.Timersのものだと思いますか?サンプルの作業プログラムを提供していただけますか?ありがとう
Fandango68

1
@ Fandango68名前空間System.Threadingのタイマーを使用しました。System.Timersには別のタイマーがあります。一番上に使っているのは間違っていると思います。
jannagy02 2018

@jannagy02月曜日のような特定の日にタスクをスケジュールする方法は?
SruthiVarghese19年


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
そして、夏時間の変更中に失敗するのを見てください。。。
ジムミッシェル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

それは私だけかもしれませんが、これらの答えのほとんどが完全ではなかったか、正しく機能しないようでした。私は非常に速くて汚いものを作りました。そうは言っても、このようにすることがどれほど良いアイデアかはわかりませんが、毎回完璧に機能します。

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
Tommy 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()フォームのロードイベントにメソッドを配置します。アプリケーションが起動すると、再起動が時間どおりに行われ、タイマーに負の値を入力しても例外が発生しないように、1日追加した再起動時間よりも大きいAppRestart場合の値が設定されます。ではどんなコードイベントの実行あなたは、その特定の時間に走ったが必要です。アプリケーションがそれ自体で再起動する場合は、必ずしもタイマーを停止する必要はありませんが、それでも問題はありません。アプリケーションが再起動しない場合は、メソッドを再度呼び出すだけで問題ありません。currentAppRestarttmrRestart_ElapsedRestartApp()


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、実行!

以下のコードを使用して、1日に1回サービスを自動的に再起動します。タイマーは長期間にわたって信頼性が低いことがわかったため、スレッドを使用しますが、この例ではコストが高くなりますが、この目的のために作成されたのはタイマーだけなので、これは問題ではありません。

(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);
    }
}

最小間隔を1000ミリ秒に設定していることに注意してください。これは、必要な精度に応じて、拡大、縮小、または削除できます。

アプリケーションが閉じたら、スレッド/タイマーも停止することを忘れないでください。


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

1つのタスクの簡単な例:

using System;
using System.Timers;

namespace ConsoleApp
{
    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();
        }
    }

    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");
            }
        }
    }
}

-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.