web-dev-qa-db-ja.com

定期的に実行されるWindowsサービス

Xサービスごとに実行されるWindowsサービスを作成しています。完了するプロセスはかなり集中的であるため、バックグラウンドワーカーを使用したいと考えています。設定ファイルを使用して、実行間の時間とサービスが最後に実行された時間の両方を保存しています。

これを行うための最良の方法は正確にはわかりません。つまり、できるだけ少ないリソースを使用してサービスをアイドル状態にし、実行時にバックグラウンドワーカーで実行して、何をしたかを報告する必要があります。アイドルモードに戻ります。

2つのバックグラウンドワーカーの使用を検討しました。最初のワーカーは、次のようなものを実行するサービスのプライベートローカル変数になります。

while (true)
{
      //create new background worker and run

      Thread.Sleep(Settings.Default.SleepTimeHours * 3600000);
}

ループの各反復で作成され、完了すると破棄されるサブワーカーを使用します。キャンセルをサポートするには、サービスで利用可能な2番目のワーカーのローカルインスタンスが必要になると思いますが、プロセスが現在実行されていない場合はnullになります。セカンダリワーカーが完了すると、レポートが送信され、最後の実行時間が設定ファイルに設定されてから、ワーカーが破棄され、参照がnullに設定されます。

これを行うためのより良い方法またはベストプラクティスがあるかどうか私は思っています。

ありがとう

22
Josh

通常はタイマーを使用し、プロセスの実行が開始したらタイマーを停止します。

方法 について説明する記事を次に示します。

34
CitizenBane

"SleepTimeHours"の全期間にわたってスレッドをロックし、その間にサービスを停止することさえできないため、これはあまり良い考えではありません。

このサービスを作成して、スリープ状態になるようにすることもできます。 5秒後、作業に戻る時間かどうかを確認し、そうでない場合は、さらに5秒スリープします(サービスを停止する必要がある場合に必要な応答性が得られます)。

または:x時間ごとに実行するために、Windowsの「スケジュールされたタスク」機能を使用してスケジュールできるコンソールアプリを作成する方がよい場合があります。そうすれば、アプリが何も実行していない場合でも、システムリソースをブロックしたり使用したりすることはありません......

マーク

15
marc_s

Quartz.Netのようなジョブスケジューラについて考えてみましょう。

http://quartznet.sourceforge.net/

10
Austin Salonen

このようなものはどうですか:

    public class LongRunningService : ServiceBase
{
    System.Threading.Thread processThread;
    System.Timers.Timer timer;
    private Boolean Cancel;

    protected override void OnStart(string[] args)
    {
        timer = new Timer(Settings.Default.SleepTimeHours * 3600000);
        timer.Elapsed += new ElapsedEventHandler(timer_Tick);
        timer.Start();

        Cancel = false;
    }

    protected override void OnContinue()
    {
        timer.Start();
    }

    protected override void OnPause()
    {
        timer.Stop();
    }

    protected override void OnStop()
    {
        if (processThread.ThreadState == System.Threading.ThreadState.Running)
        {
            Cancel = true;
            // Give thread a chance to stop
            processThread.Join(500);
            processThread.Abort();
        }
    }

    void timer_Tick(object sender, EventArgs e)
    {
        processThread = new System.Threading.Thread(new ThreadStart(DoWork));
        processThread.Start();
    }

    private void DoWork()
    {
        try
        {
            while (!Cancel)
            {

            if (Cancel) { return; }
            // Do General Work

            System.Threading.Thread.BeginCriticalRegion();
            {
                // Do work that should not be aborted in here.
            }
            System.Threading.Thread.EndCriticalRegion();
            }

        }
        catch (System.Threading.ThreadAbortException tae)
        {
            // Clean up correctly to leave program in stable state.
        }
    }
}
7
Ian

System.Threading.WaitHandleアプローチに基づくアプローチを使用します。

using System.Threading;
private Thread _thread;
private ManualResetEvent _shutdownEvent = new ManualResetEvent(false);
private ManualResetEvent _scheduleEvent = new ManualResetEvent(false);
private System.Timers.Timer _scheduleTimer = new System.Timers.Timer();
protected override void OnStart(string[] args)
{
    // Configure the timer.
    _scheduleTimer.AutoReset = false;
    _scheduleTimer.Interval = 120000; // 2 minutes in milliseconds
    _scheduleTimer.Elapsed += delegate { _scheduleEvent.Set(); }

    // Create the thread using anonymous method.
    _thread = new Thread( delegate() {
        // Create the WaitHandle array.
        WaitHandler[] handles = new WaitHandle[] {
            _shutdownEvent,
            _scheduleEvent
        };
        // Start the timer.
        _scheduleTimer.Start();
        // Wait for one of the events to occur.
        while (!_shutdownEvent.WaitOne(0)) {
            switch (WaitHandle.WaitAny(handles)) { 
               case 0:  // Shutdown Event
                   break;
               case 1:  // Schedule Event
                   _scheduleTimer.Stop();
                   _scheduleEvent.Reset();
                   ThreadPool.QueueUserWorkItem(PerformScheduledWork, null);
                   break;
               default:
                   _shutdownEvent.Set(); // should never occur
            }
        }
    } );
    _thread.IsBackground = true;
    _thread.Start();
}
protected override void OnStop()
{
    // Signal the thread to shutdown.
    _shutdownEvent.Set();
    // Give the thread 3 seconds to terminate.
    if (!_thread.Join(3000)) {
        _thread.Abort(); // not perferred, but the service is closing anyway
    }
}
private void PerformScheduledWork(object state)
{
    // Perform your work here, but be mindful of the _shutdownEvent in case
    // the service is shutting down.
    //
    // Reschedule the work to be performed.
     _scheduleTimer.Start();
}
5
Matt Davis

誰かがスーパーユーザーについて同様の質問をしました。 Windowsサービスを監視するツールをインストールできます。 Service Hawk のようなものは、サービスを開始したままにしたり、サービスをスムーズに実行し続けるために自動再起動(おそらく夜間)をスケジュールしたりするのに役立ちます。

2
user188162

バックグラウンドワーカーを使用したくない場合。バックグラウンドワーカーは、何かがフォアグラウンド(UIなど)で実行されており、バックグラウンドで他の何かを実行する場合にも使用されます。あなたのケースではフォアグラウンド処理はありません、あなたが持っているのは時々実行する必要がある単一のジョブです。

また、眠っているビジネスなどを気にしないでください。代わりに、 Quartz.NET などのスケジューラフレームワークを使用して、たまに目覚めさせてください。このようにして、サービスが開始すると、スケジューラーが初期化され、スケジューラーが起動するまで何も実行されません。スケジューラーがウェイクアップすると、初期化時に呼び出すように指示したオブジェクトのメソッドが呼び出されます。

このようにして、サービスはアイドル時にCPUをほとんど消費せず、必要なときに完全なSteamで動作します。

2
zvolkov

Blog.StackOverflow.comには、キャッシュの有効期限を使用して定期的なタスクを処理する方法に関する興味深い記事があります。

http://blog.stackoverflow.com/2008/07/easy-background-tasks-in-aspnet/

2
Keltex

約1時間前に同僚と同じ議論をしました!私はwhile(_isPolling)オプションを使用しました。同期的に実行するために作業が必要だったからです。同じ作業を他のスレッド(Timerアプローチ)で取り上げたくありませんでした。そのために追加のロックを実装することは無駄に思えました。

1
David Kiff

私の会社では、Windowsタスクスケジューラを使用してサービスを開始し、サービス自体をシャットダウンしています。これにより、サービスが常に適切なタイミングで実行され、スケジューリングシステムによって成功/失敗などのレポートが可能になります。

1
JustLoren

これを行うにはタイマーを使用します。

1
RichardOD

たくさんのチュートリアルをチェックした後、60分ごとに定期的に実行するWindowsサービスを開発しました。バックグラウンドでプロセスを開始し、Test.exeを実行します。

public partial class Scheduler : ServiceBase
{
    private Timer timer1 = null;


    public Scheduler()
    {
        InitializeComponent();
    }
    // Get the Exe path
    private string GetPath()
    {
        Assembly assembly = Assembly.GetExecutingAssembly();
        return Path.Combine(Path.GetDirectoryName(Assembly.Location), "Test.exe");

    }
    // you can right your own custom code
    // Above Vista version the process works as Administrator- "runas" and in old OS it will Start process as per Administartor rights  
    public void startprocess()
    {
       // System.Diagnostics.Process.Start(GetPath());
        System.Diagnostics.Process process = null;
        System.Diagnostics.ProcessStartInfo processStartInfo;

        processStartInfo = new System.Diagnostics.ProcessStartInfo();

        processStartInfo.FileName = GetPath();

        if (System.Environment.OSVersion.Version.Major >= 6)  // Windows Vista or higher
        {
            processStartInfo.Verb = "runas";
        }
        else
        {
            // No need to Prompt to run as admin
        }

        processStartInfo.Arguments = "";

        process = System.Diagnostics.Process.Start(processStartInfo);

    }
    // On start method will load when system Start 
    // timer1_tick Event will call every hour after once start the service automatically
    protected override void OnStart(string[] args)
    {
        //System.Diagnostics.Debugger.Launch();
        timer1 = new Timer();
        this.timer1.Interval = 3600000; //every 60 min
        this.timer1.Elapsed += new System.Timers.ElapsedEventHandler(this.timer1_Tick);
        timer1.Enabled = true;

    }

    // Timer1_tick will Start process every hour
    private void timer1_Tick(object sender, ElapsedEventArgs e)
    {
        //Write code here to do some job depends on your requirement
        startprocess();
    }
    // Method will stop the service
    // we have set the method stop service as false. so service will not stop and run every hour 
    protected override void OnStop()
    {
        timer1.Enabled = false;

    }

}

0
mihir patel

タイマーとワーカースレッドの組み合わせを適用しましたが、これまでのところ問題なく動作します。

タイマーを1分ごとにチェックするように設定し、時間がスケジュールの時間と一致する場合は、ワーカースレッドを作成します。

私はいくつかのハウスキーピングを行う際にこのテクニックを使用しました:

if (nowTimeVar.ToShortTimeString().CompareTo(houseKeep.ToShortTimeString()) == 0)
{
    myHouseKeeper = new clsHouseKeep(archiveFolder, lastday, myCounter, myLogger);                
}    
0
Tom