毎日タスクを実行するようにC#Windowsサービスをスケジュールするにはどうすればよいですか?


84

C#(。NET 1.1)で記述されたサービスがあり、毎晩深夜にクリーンアップアクションを実行したいと考えています。すべてのコードをサービス内に含める必要があるので、これを実現する最も簡単な方法は何ですか?Thread.Sleep()ロールオーバーする時間の使用とチェック?


10
あなたは本当に一日中メモリに置かれ、真夜中に何かをする.NETプロセスを作りたいですか?ツールのインストール時に、アプリを起動し、処理を実行してから消える深夜(またはその他の構成可能な時間)に起動するシステムイベントを設定できないのはなぜですか?
ロバートP

6
20〜40メガバイトのメモリを消費し、常に実行されているマネージドサービスがあり、1日1回しか実行されていないことに気付いたら、少し腹を立てるでしょう。:)
ロバートP

その複製されたリンク
csa 2016

回答:


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
毎分目を覚まして時間を確認するのではなく、タイマーを深夜に期限切れにするように設定する方が理にかなっていますか?
Kibbee 2009

11
@Kibbee:サービスが深夜に実行されていない場合(何らかの理由で)、サービスが再び実行されたらすぐにタスクを実行することをお勧めします。
M4N 2011年

4
次に、サービスの起動時にコードを追加して、サービスが実行されていなかったため、すぐに実行する必要があるかどうかを判断する必要があります。10分ごとに継続的にチェックするべきではありません。起動時に実行する必要があるかどうかを確認し、実行しない場合は次に実行する時間を決定します。その後、必要なだけタイマーを設定します。
Kibbee 2011年

3
@Kibbee:はい、同意します(これは、私たちが話し合っている小さな詳細です)。ただし、タイマーが10秒ごとに起動しても、害はありません(つまり、時間はかかりません)。
M4N 2011年

1
良い解決策ですが、上記のコードには_timer.start()がなく、_lastRunは次のように昨日に設定する必要があります。privateDateTime_lastRun = DateTime.Now.AddDays(-1);
ズールー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
良い提案-基本的なタイマーのものよりも少し複雑なことをしたらすぐに、クォーツを見る必要があります。
serg10 2009

2
クォーツはとても使いやすいので、非常に単純なタスクであっても、先に進んでそれを使用するだけだと私は主張します。スケジュールを簡単に調整できるようになります。
アンディホワイト

超シンプルじゃないですか?ほとんどの初心者はここでこの問題をキャッチしますstackoverflow.com/questions/24628372/…–
Emil

21

毎日の仕事?スケジュールされたタスク(コントロールパネル)である必要があるようです。ここではサービスは必要ありません。


15

実際のサービスである必要がありますか?Windowsのコントロールパネルで組み込みのスケジュールされたタスクを使用できますか?


サービスはすでに存在するため、そこに機能を追加する方が簡単な場合があります。
M4N 2009

1
このような狭い目的のサービスをコンソールアプリに変換するのは簡単なことです。
スティーブンマーティン

7
彼はすでに「すべてのコードをサービスに含める必要がある」と述べています。元の要件に疑問を呈する必要はないと思います。状況によって異なりますが、スケジュールされたタスクに対してサービスを使用する非常に正当な理由がある場合があります。
jeremcc 2009

3
+1:あなたは常にあなたの問題をあらゆる側面から見る必要があるからです。
gvS 2009

6
本当に1日に実行するタスクが1つしかない場合は、スケジュールされたタスクで実行する単純なコンソールアプリで十分だと思います。私のポイントは、それはすべて状況に依存し、「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 2009

2

他の人がすでに書いたように、タイマーはあなたが説明したシナリオで最良のオプションです。

正確な要件によっては、1分ごとに現在の時刻を確認する必要がない場合があります。真夜中に正確にアクションを実行する必要はないが、真夜中から1時間以内であれば、日付が変わっていないかどうかだけをチェックするというマーティンのアプローチに進むことができます。

深夜にアクションを実行する理由が、コンピューターのワークロードが低いと予想される場合は、注意が必要です。同じ仮定が他のユーザーによって行われることが多く、突然、0:00から0の間に100のクリーンアップアクションが開始されます。 :01 am

その場合は、別の時間にクリーンアップを開始することを検討する必要があります。私は通常、これらのことを時計の時間ではなく、30分に行います(1.30が私の個人的な好みです)


1

タイマーを使用することをお勧めしますが、分ではなく45秒ごとにチェックするように設定してください。そうしないと、負荷が高く、特定の分のチェックが見落とされる状況に遭遇する可能性があります。タイマーがトリガーされてからコードが実行されて現在の時刻がチェックされるまでの間に、ターゲットの分が見落とされた可能性があるためです。


まだ1回実行されていないことを確認していない場合、45秒ごとに確認していると、00:00を2回ヒットする可能性があります。
Michael Meadows

いい視点ね。この問題は、チェック手順の最後にSleep(15000)を呼び出すことで回避できます。(または、念のために16000ミリ秒;-)
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.