0

特定の時間待機する必要があるアプリケーションがありますが、必要に応じて現在の操作をキャンセルできる必要もあります。次のコードがあります。

private void waitTimer(int days)
{
    TimeSpan waitTime = TimeSpan.FromDays(days);
    System.Timers.Timer timer = new System.Timers.Timer(waitTime.TotalMilliseconds);   // Wait for some number of milliseconds
    timer.Enabled = true;
    timer.Start();
    timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); // Subscribe to event handler

    while (!TimerSettings.TimerFinished && !quitToken.IsCancellationRequested);  // Loop forever untill timer is finished or operation is cancled. 

    timer.Elapsed -= new ElapsedEventHandler(OnTimedEvent); // Unsubscribe

    DoWork(); // Do work when timer finishes.......
}

以下は、タイマー終了イベントのイベント ハンドラーです。

private void OnTimedEvent(object obj, ElapsedEventArgs e)
{
    TimerSettings.TimerFinished = true;
}

while ループは、タイマーが終了するまで、またはキャンセル要求が入力されるまで無限にループします。この機能を保持したいのですが、タイマーが終了するのを待っている間、永遠にループしたくありません。私のタイマーは、数日の間隔で実行するように設定できるため、長時間ループしても意味がありません。

これを行う別の方法はありますか?

私はできることを知っています:

Thread.Sleep(runDuration.TotalMilliseconds);

ただし、これはブロックされ、キャンセル要求を出すことができません。

編集:ここで一時停止する必要があるもの/理由を詳しく説明するために、アプリケーションのより詳細な説明があります。基本的には定期的に「仕事」をするアプリが欲しいです。したがって、以下の回答の1つに基づいて、次のようなことをした場合:

class Program
{
    // Do something in this method forever on a regular interval 
    //(could be every 5min or maybe every 5days, it's up to the user)
    static void Main(string[] args)
    {
        while(true)
        {
          if(args?.Length > 0)
              waitTimer(args[0]);
          else 
              wiatTimer(TimeSpan.FromDays(1).TotalSeconds); // Default to one day interval
        }             
    }

private void waitTimer(int numIntervals)
{
    this.ElapsedIntervals = 0;
    this.IntervalsRequired = numIntervals;
    this.timer = new System.Timers.Timer(1000);   // raise the elapsed event every second
    timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); // Subscribe to event handler
    //timer.Enabled = true; timer.Start() does this for you, don't do this
    timer.Start();
    //thats all here
}

 private void OnTimedEvent(object obj, ElapsedEventArgs e)
 {
    this.ElapsedIntervals += 1;
    if(this.CancelRequested)
    {
       this.ElapsedIntervals = 0;
       this.timer.Stop();
       return;
    }
    if(this.ElapsedIntervals >= this.IntervalsRequired)
    {
       this.ElapsedIntervals = 0;
       this.timer.Stop();
       DoWork();   // This is where my work gets done.......
      return;
    }
  }
}

次に、サービス/コンソール アプリが起動し、終日タイマーを設定する無限ループに入ります。以前は、次の場所で他のコードの実行を実際に停止していました。

while (!TimerSettings.TimerFinished && !quitToken.IsCancellationRequested);

少なくとも機能しましたが、前述のように、スレッドを一時停止するためのリソースを集中的に使用する方法になる可能性があります。基本的に、私が本当に必要としているのは、タイマーが切れるまでスレッドをブロックする方法です。

EDIT2:これは、待機ハンドルを使用して機能するように見える私の最終的な実装です...

class TimerClass
{
    /// <summary>
    /// Initialize new timer. To set timer duration,
    /// either set the "IntervalMinutes" app config 
    /// parameter, or pass in the duration timespan.
    /// </summary>
    /// <param name="time"></param>
    internal bool StartTimer(CancellationToken quitToken, TimeSpan? duration = null)
    {
        TimeSpan runDuration = new TimeSpan();
        runDuration = duration == null ? GetTimerSpan() : default(TimeSpan);

        if (runDuration != default(TimeSpan))
        {
            WaitTimer(runDuration); // Waits for the runduration to pass
        }
        return true;
    }

    /// <summary>
    /// Get duration to run the timer for.
    /// </summary>
    internal TimeSpan GetTimerSpan()
    {
        TimerSettings.Mode = App.Settings.Mode;
        DateTime scheduledTime = new DateTime();

        switch (TimerSettings.Mode)
        {
            case "Daily":
                scheduledTime = DateTime.ParseExact(App.Settings.ScheduledTime, "HH:mm:ss", CultureInfo.InvariantCulture);
                if (scheduledTime > DateTime.Now)
                    TimerSettings.TimerInterval = scheduledTime - DateTime.Now;
                else
                    TimerSettings.TimerInterval = (scheduledTime + TimeSpan.FromDays(1)) - DateTime.Now;
                break;
            case "Interval":
                double IntervalMin = double.TryParse(App.Settings.PollingIntervalMinutes, out IntervalMin) ? IntervalMin : 15.00;
                int IntervalSec = Convert.ToInt32(Math.Round(60 * IntervalMin));
                TimeSpan RunInterval = new TimeSpan(0, 0, IntervalSec);
                TimerSettings.TimerInterval = RunInterval;
                break;
            case "Manual":
                TimerSettings.TimerInterval = TimeSpan.FromMilliseconds(0);
                break;
            default:
                TimerSettings.TimerInterval = (DateTime.Today + TimeSpan.FromDays(1)) - DateTime.Now;
                break;
        }
        return TimerSettings.TimerInterval;
    }

    /// <summary>
    /// Event handler for each timer tick.
    /// </summary>
    /// <param name="obj"></param>
    /// <param name="e"></param>
    private void OnTimedEvent(object obj, ElapsedEventArgs e)
    {
        ElapsedIntervals += 1;
        if (CancelRequested.IsCancellationRequested) // If the application was cancled
        {
            ElapsedIntervals = 0;
            timer.Stop();
            WaitHandle.Set();
            return;
        }
        if (ElapsedIntervals >= IntervalsRequired) // If time is up
        {
            ElapsedIntervals = 0;
            timer.Stop();
            WaitHandle.Set();
            return;
        }
    }

    /// <summary>
    /// Timer method to wait for a
    /// specified duration to pass. 
    /// </summary>
    /// <param name="span"></param>
    private void WaitTimer(TimeSpan span)
    {
        WaitHandle = new AutoResetEvent(false);
        int tickDuration = 1000;  // Number of milliseconds for each tick
        IntervalsRequired = Convert.ToInt64(span.TotalMilliseconds / (tickDuration > 0 ? tickDuration : 0.01));
        timer = new System.Timers.Timer(tickDuration);          // Raise the elapsed event every tick
        timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); // Subscribe to event handler for when each tick is complete
        timer.Start();           // Start ticking
        WaitHandle.WaitOne();    // Halt the main thread untill span is reached
    }


    // Timer parameters: 
    private static long ElapsedIntervals { get; set; }
    private static long IntervalsRequired { get; set; }
    private static System.Timers.Timer timer { get; set; }
    private static CancellationToken CancelRequested { get; set; }
    private static string Mode { get; set; }
    private static TimeSpan TimerInterval { get; set; }
    private static EventWaitHandle WaitHandle { get; set; }
}

internal static class TimerSettings
{
    internal static string Mode { get; set; }
    internal static TimeSpan TimerInterval { get; set; }
}
4

2 に答える 2

0

Timer.Elapsed Event のドキュメントを参照してください。このイベントは、AutoReset プロパティが true (デフォルト) に設定されている間、間隔が経過するたびに繰り返し発生します。経過した間隔の数を独自にカウントし、それをこのイベント ハンドラーで必要な経過間隔と比較して、タイマーを停止する時間であるかどうかを確認します。その際はキャンセルも承ります。タイマーが必要な間隔数を終了したら、そのイベント ハンドラーから doWork 関数を呼び出すことができます。

private void waitTimer(int numIntervals)
{
    this.ElapsedIntervals = 0;
    this.IntervalsRequired = numIntervals;
    this.timer = new System.Timers.Timer(1000);   // raise the elapsed event every second
    timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); // Subscribe to event handler
    //timer.Enabled = true; timer.Start() does this for you, don't do this
    timer.Start();
    //thats all here
}

private void OnTimedEvent(object obj, ElapsedEventArgs e)
{
    this.ElapsedIntervals += 1;
    if(this.CancelRequested)
    {
        this.ElapsedIntervals = 0;
        this.timer.Stop();
        return;
    }
    if(this.ElapsedIntervals >= this.IntervalsRequired)
    {
       this.ElapsedIntervals = 0;
       this.timer.Stop();
       DoWork();
       return;
    }
}

https://msdn.microsoft.com/en-us/library/system.timers.timer.elapsed(v=vs.110).aspx

私が見ているように、「一時停止」に関しては、一時停止したい理由が 2 つありますが、どちらがあなたの理由なのかわかりません。

  1. アプリケーションが実行を「終了」して正常に終了するのを防ぎたい。
  2. 必要な間隔の数が経過するまで、他のコードの実行を保留したい

理由が 2 の場合、この回答は完了です。

于 2016-07-07T19:43:59.033 に答える
-3

まず第一に、「あなたは絶対に(!)何も「ビジーウェイト」したくありません!」(BAD DOG! NO ビスケット!)

ええと...

この問題の実用的な解決策は、実際のタイマーを使用する代わりに、セマフォ (またはその他の適切な相互排除オブジェクト) で時間指定待機を実行することです。終了する前に待機から抜け出す必要がある場合は、待機中のものをストロボします。

あなたの現在の「解決策」の重大な問題は、プロセスを100% の CPU 使用率にし、まったく無駄にすることです。 そんなことは絶対にしないでください!

于 2016-07-07T19:19:56.653 に答える