web-dev-qa-db-ja.com

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

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

これに関する情報を追加する必要があります。

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

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

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

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

70
Vimvq1987
  • 探していることを実行するコンソールアプリを作成する
  • Windowsの「 Scheduled Tasks 」機能を使用して、実行する必要があるときにコンソールアプリを実行する

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

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

  • Windows Formsアプリでは、 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();
    }
}
72
marc_s

そのような機能を必要とするアプリケーションを作成するときはいつでも、私は、見つけた単純な.NETライブラリを介して常にWindowsタスクスケジューラを使用します。

お願い 同様の質問に対する私の回答をご覧くださいサンプルコードと詳細な説明について

9
Maxim Zaslavsky

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

新しいタスクのスケジューリングはとても簡単です。例:11:52では、最初のタスクは15秒ごと、2番目の例は5秒ごとです。毎日実行する場合は、3パラメーターに24を設定します。

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);
    }
}
8
jannagy02

他の人が言ったように、コンソールアプリを使用して、スケジュールされたときに実行できます。他の人が言っていないことは、このアプリで、メインアプリケーションで待機しているクロスプロセス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();
    }
}
8
Courtney D

私はこれが古いことを知っていますが、これはどうですか:

次の実行時間までの時間を計算する起動時に起動するタイマーを作成します。ランタイムの最初の呼び出しで、最初のタイマーをキャンセルし、新しいデイリータイマーを開始します。毎日から毎時、または周期性を希望するものに変更します。

4
mansoor

私が知っているおそらく最も簡単な方法は、Windowsタスクスケジューラを使用して特定の時刻にコードを実行するか、アプリケーションを永続的に実行して特定の時刻を確認するか、Windowsサービスを作成することです。同じ。

4

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
Gino

実行可能ファイルを実行する場合は、Windowsスケジュールタスクを使用します。現在のプログラムでメソッドを実行する必要があると(おそらく誤って)仮定します。

メソッドが呼び出された最後の日付を保存するスレッドを連続して実行しないのはなぜですか?

(たとえば)1分ごとに起動し、現在の時刻が指定した時刻よりも大きく、保存されている最後の日付が現在の日付でない場合は、メソッドを呼び出して日付を更新します。

1
paxdiablo

私は最近、毎日再起動する必要があるc#アプリを作成しました。私はこの質問が古いことを知っていますが、別の可能な解決策を追加することは痛いとは思いません。これは、指定された時間に毎日の再起動を処理する方法です。

_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()を入れます。アプリケーションの起動時に、AppRestartが再起動時間よりも大きい場合、currentの値を設定します。1日をAppRestartに追加して、再起動が時間通りに行われるようにします。タイマーに負の値を入れても例外は発生しません。 _tmrRestart_Elapsed_イベントで、その特定の時間に実行した必要なコードを実行します。アプリケーションが独自に再起動する場合、必ずしもタイマーを停止する必要はありませんが、どちらも害はありません。アプリケーションが再起動しない場合は、単にRestartApp()メソッドを再度呼び出してください。 。

1
Fancy_Mammoth

それは私かもしれませんが、これらの答えのほとんどは完全ではないか、正しく機能しないように思われました。私は何か非常に迅速で汚れたものを作りました。この方法でそれを行うのがどれほど良いアイデアかはわかりませんが、毎回完全に機能します。

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
Deathstalker

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

60分ごとに1秒ごとに実行するように時間を設定するのではなく、残り時間を計算し、タイマーをこの半分(または他の一部)に設定できます。この方法では、時間をあまりチェックせず、タイマー間隔が目標時間に近づくにつれて精度を維持します。

たとえば、今から60分後に何かをしたい場合、タイマーの間隔は近似値になります。

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

以下のコードを使用して、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
apc

これには簡単なアプローチがあります。これにより、アクションが発生する前に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
Gustav Bok

私はこれが非常に便利だと感じました:

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
JoKeRxbLaCk