1

Windows サービスとタイマーの使用に関連する多くの投稿を読みましたが、イベント ハンドラーがまだトリガーされている理由がわかりません。誰かが私を正しい方向に向けることができますか? なぜこれが起こっているのかを知りたいので、将来これを回避する方法を理解しています。

編集: onError イベント ハンドラーが呼び出されることはありません (または、イベント ログにイベントが表示されます)。

タイマー: System.Timers.Timer
ServiceBase: System.ServiceProcess.ServiceBase


抽象クラスは次のとおりです。

public abstract class ABCService : ServiceBase
{
    // Members
    protected Timer             invocationTimer;
    protected Timer             wellnessTimer;
    protected FileSystemWatcher fsw;


    // Constructors
    protected ABCService()
    {
        invocationTimer = new Timer();
        wellnessTimer   = new Timer();
        fsw             = null;

        invocationTimer.AutoReset = false;
        invocationTimer.Interval  = 30000; // 30 seconds
        invocationTimer.Elapsed  += new ElapsedEventHandler(invocationTimer_Elapsed);

        wellnessTimer.AutoReset   = false;
        wellnessTimer.Elapsed    += new ElapsedEventHandler(wellnessTimer_Elapsed);
    }


    // Methods
    protected void invocationTimer_Elapsed(object o, ElapsedEventArgs args)
    {
        try
        {
            // log to event log

            invocationTimer.Stop();

            if ((new FileInfo(fsw.Path + "\\" + fsw.Filter)).Exists)
            {
                onCreated(this, new FileSystemEventArgs(WatcherChangeTypes.Created, fsw.Path, fsw.Filter));
            }
        }
        catch (Exception x)
        {
            onError(this, new ErrorEventArgs(x));
        }
    }

    protected void wellnessTimer_Elapsed(object o, ElapsedEventArgs args)
    {
        try
        {
            // log to event log

            wellnessTimer.Stop();
            wellnessTimer.Interval = 60000; // ms

            if (fsw != null)
            {
                fsw.Dispose();
            }

            fsw = new FileSystemWatcher(ConfigurationManager.AppSettings["pathKey"], ConfigurationManager.AppSettings["filterKey"]);

            invocationTimer.Start();
        }
        catch (Exception x)
        {
            onError(this, new ErrorEventArgs(x));
        }
    }

    protected abstract void onCreated(object o, FileSystemEventArgs args);

    protected virtual void onError(object o, ErrorEventArgs args)
    {
        // log to event log

        wellnessTimer.Start();
    }

    protected override void OnStart(string[] args)
    {
        // log to event log

        wellnessTimer.Interval = 5000; // 5 seconds
        wellnessTimer.Start();
    }

    protected override void OnStop()
    {
        // log to event log

        wellnessTimer.Stop();
    }
}

インスタンスクラスは次のとおりです。

public partial class Service1 : ABCService
{
    // Members
    private static object locket = new object();
    private static DateTime LAST_RUN_TIME = DateTime.Now.AddSeconds(-10);


    // Constructors
    public Service1()
    {
        InitializeComponent();
    }


    // Methods
    protected override void onCreated(object o, FileSystemEventArgs args)
    {
        lock (locket)
        {
            // log to event log

            if ((DateTime.Now - LAST_RUN_TIME).Seconds >= 10)
            {
                // do stuff
            }
            else
            {
                // log to event log

                invocationTimer.Stop();
                invocationTimer.Start();
            }
        }
    }
}

部分クラスの自動生成コードは次のとおりです。

partial class Service1
{
    /// <summary> 
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region Component Designer generated code

    /// <summary> 
    /// Required method for Designer support - do not modify 
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
        // 
        // Service1
        // 
        this.ServiceName = "Service1";

    }

    #endregion
}

では、正確には何が起こっているのでしょうか。イベント ログを確認すると、1 分に 1 回、wellnessTimer イベント ハンドラが呼び出されていることがわかります。

これが私が起こっていると思うことですが、明らかに間違っています:

1. Service is started via MMC
2. OnStart() method is invoked
3. wellnessTimer interval is set to 5 seconds
4. wellnessTimer start method is invoked
5. wellnessTimer_Elapsed event handler is invoked
6. wellnessTimer stop method is invoked
7. wellnessTimer interval is set to 5 minutes
8. invocationTimer start method is invoked
9. 30 seconds later, the invocationTimer_Elapsed method is invoked
10. invocationTimer stop method is invoked

この時点で、両方のタイマーがこのインスタンスにまだ存在している必要がありますが、無効にする必要があります。これを Visual Studio 2010 のプロセスにアタッチしてデバッグし、イベント ハンドラーに渡されるオブジェクト (送信者) の ID をマークしました。インスタンスと同じオブジェクトです。また、[ローカル] ウィンドウの両方のタイマーで、有効なプロパティが false に設定されていました。

これにより、継承を正しく使用していないか、スレッドで何かが起こっていることがわかります。私はどちらも得意ではありませんが、それが原因である場合は教えてください。学べます。

事前にみんなに感謝します。


編集#2:ここにいくつかのトレースデータがあります...

「o」は、イベント ハンドラに渡されるオブジェクトを表します

ABCService() method invoked <--
ABCService() method invoked -->

Service1() method invoked <--
Service1() method invoked -->

OnStart() method invoked <--
OnStart() method invoked -->

wellnessTimer_Elapsed() method invoked <--
((System.Timers.Timer) o).Enabled  = False
((System.Timers.Timer) o).Interval = 5000
this.wellnessTimer.Enabled  = False
this.wellnessTimer.Interval = 5000
wellnessTimer_Elapsed() method invoked -->

invocationTimer_Elapsed() method invoked <--
((System.Timers.Timer) o).Enabled  = False
((System.Timers.Timer) o).Interval = 30000
this.invocationTimer.Enabled  = False
this.invocationTimer.Interval = 30000
invocationTimer_Elapsed() method invoked -->

wellnessTimer_Elapsed() method invoked <--
((System.Timers.Timer) o).Enabled  = False
((System.Timers.Timer) o).Interval = 60000
this.wellnessTimer.Enabled  = False
this.wellnessTimer.Interval = 60000
wellnessTimer_Elapsed() method invoked -->

invocationTimer_Elapsed() method invoked <--
((System.Timers.Timer) o).Enabled  = False
((System.Timers.Timer) o).Interval = 30000
this.invocationTimer.Enabled  = False
this.invocationTimer.Interval = 30000
invocationTimer_Elapsed() method invoked -->
4

2 に答える 2

1

Timer クラスに関する MSDN のコメントから:

Elapsed イベントは、Dispose メソッドまたは Stop メソッドが呼び出された後、または Enabled プロパティが false に設定された後に発生する可能性があります。これは、Elapsed イベントを発生させるシグナルが常にスレッド プール スレッドでの実行のためにキューに入れられるためです。この競合状態を解決する 1 つの方法は、Elapsed イベントのイベント ハンドラーに後続のイベントを無視するように指示するフラグを設定することです。

したがって、タイマーが無効になった後にイベントハンドラーが実行されないように、イベントハンドラー内で次のようなことを行うことができます。

private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
    if (timer.Enabled)
    {
        Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
    }
}
于 2012-07-24T01:11:01.697 に答える
0

あなたが提供したコードとまったく同じ問題が発生していますか、それともコード周りに依存していますか:

       // depending on logic, these next two lines will be called together or not at all

コードから読み取った内容に基づいて、 onCreated 呼び出しでエラーが発生すると、ウェルネス タイマーが 60 秒ごとに起動します。これがファイル操作を行っている場合、叫ばれることの 1 つは、サービスが実行されているアカウントのセキュリティ アクセス許可です。

例外をキャッチした場合、イベントログまたはログファイルに出力します。例外による動作は、特にデバッガーを接続するのがあまり便利でない場合に、混乱を招きやすい状況につながる可能性があります。

于 2012-07-24T01:21:03.113 に答える