2

次のような動作のタイマーを作成します。

  • タスク/ジョブの処理時間がタイマー間隔未満の場合、(timer.interval - ジョブ/ジョブの処理時間) でタイマーを開始します

  • ジョブ/タスクの処理時間がタイマー間隔を超えている場合は、次のジョブ/タスクをすぐに開始します

以下のコードは機能しますが、ElapsedEventHandler メソッドでジョブ/タスクを最初に実行してから、新しいタイマー間隔を設定できる理由を知りたいです。間隔が経過すると、System.Timers.Timer の Elapsed イベントが発生します。オプション AutoReset = false を使用して、最初の Interval が経過した後、Timer が Elapsed イベントを 1 回だけ発生させるように設定します。その後、Timer.Start() を手動で呼び出して、再度開始する必要があります。

using System;
using System.Timers;

 namespace TestTimer
 {
    class Program
    {
        private static Timer t;
        private static double intervalMiliseconds;

        static void Main(string[] args)
        {
            intervalMiliseconds = 5000; // 5 seconds

            t           = new Timer();
            t.Interval  = intervalMiliseconds;
            t.AutoReset = false;
            t.Elapsed  += new ElapsedEventHandler(OnTimedEvent);
            t.Start();

            log("Timer started at " + DateTime.Now.ToString());
            log("Interval is: " + defaultIntervalMiliseconds);
            Console.ReadKey();
        }

        private static void log(string text)
        {
            Console.WriteLine(text + "\n");
        }

        private static void OnTimedEvent(object source, ElapsedEventArgs e)
        {
            // if t.Interval is set here thread just kills the job if it
            // runs longer than interval
            t.Interval = intervalMiliseconds;
            log("ElapsedEvent triggered at " + DateTime.Now.ToString());

            // job
            DateTime startTime = DateTime.Now;
            log("job started" );
            System.Threading.Thread.Sleep(8000); // 8 sec
            log("job ended" );
            TimeSpan jobTime = DateTime.Now.Subtract(startTime);
            log("job took " + jobTime.TotalSeconds + " seconds");

            // if we set t.Interval here it works so first the job
            // must be done and than we can set timer interval ? why ?
            //t.Interval = intervalMiliseconds;

            if (jobTime.TotalMilliseconds < t.Interval)
            {
                t.Interval = t.Interval - jobTime.TotalMilliseconds;
                log("Job ended Earlier starting Event in: " + t.Interval);
            }
            else
            {
                t.Interval = 100;
                log("Job overpass interval. Current time: " +
                     DateTime.Now.ToString());
            }

            t.AutoReset = false;
            t.Start();
        }
    }
}

この結果:

ここに画像の説明を入力

メソッド OnTimedEvent の開始時に t.Interval をコメントし、ジョブが完了した後に t.Interval のコメントを外すと、すべてが機能します。この結果:

ここに画像の説明を入力

メソッド OnTimedEvent の開始時にタイマー間隔を設定できないのはなぜですか。タスク/ジョブがタイマー間隔よりも長く実行された場合、スレッドは単にジョブを強制終了します。誰かが何かアイデアを持っていれば、本当に感謝していますか? これは、スレッドとメインスレッド(タイマーが実行される)の同期と関係がありますか? メソッド OnTimedEvent を呼び出すと、タイマーは AutoReset = false であるため、そのメソッドを再度呼び出すことはありません。タイマーのプロパティを設定すると、どのような違いが生じるでしょうか?

4

2 に答える 2

4
   t.Interval = intervalMiliseconds;

まさにトラブルメーカーです。これは非常に直感的でない動作であり、Timer.Interval に関するMSDN の記事で注意事項として具体的に警告されています。

Enabled と AutoReset の両方が false に設定されていて、タイマーが以前に有効になっている場合、Interval プロパティを設定すると、Enabled プロパティが true に設定されているかのように、Elapsed イベントが 1 回発生します。イベントを発生させずに間隔を設定するには、AutoReset プロパティを一時的に true に設定します。

これはかなりばかげたハックですが、機能します。値の割り当てを遅らせるだけで、確かにそれを行うより良い方法です。AutoReset = false を取得しているため、タイマーが動作しません。

System.Threading.Timer は、癖の少ない優れたタイマー クラスです。コールバック メソッドで診断を行わずに例外を飲み込むことはありません。あなたのコードは非常に敏感ですが、例外が t.Start() 呼び出しをバイパスするため、タイマーの動作が停止し、その理由がわかりません。強く推奨する。

于 2013-08-04T15:30:26.723 に答える