web-dev-qa-db-ja.com

.NET、毎分(毎分)のイベント。タイマーが最良の選択肢ですか?

私はc#を使用してWindowsフォームアプリで毎分(クロックで)何かをしたいです。私はそれについて行くための最良の方法は何だろうと思っていますか?

タイマーを使用してその間隔を60000に設定することもできますが、その分に実行するには、実際に実行可能ではなく、その分に正確に有効にする必要があります。

タイマーを使用して、間隔を1000に設定できます。次に、ティックイベント内で、設定された変数に対して現在の分をチェックし、分が変更された場合にコードを実行します。 1分ごとに作業を実行するために、コンピューターに1秒ごとにチェックを実行させているため、これは心配です。確かにこれはいですか?

私はWindowsフォームと.Net 2.0を使用しているので、.Net 3.5に付属のDispatchTimerを使用したくない

これはかなり一般的な問題であるに違いありません。これを行うより良い方法がありますか?

39
Kieran

ドリフトする可能性があり、1分以内に正確に刻まないaquinasからの回答に基づいて構築します。

static System.Timers.Timer t;

static void Main(string[] args)
{
    t = new System.Timers.Timer();
    t.AutoReset = false;
    t.Elapsed += new System.Timers.ElapsedEventHandler(t_Elapsed);
    t.Interval = GetInterval();
    t.Start();
    Console.ReadLine();
}

static double GetInterval()
{
    DateTime now = DateTime.Now;
    return ((60 - now.Second) * 1000 - now.Millisecond);
}

static void t_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    Console.WriteLine(DateTime.Now.ToString("o"));
    t.Interval = GetInterval();
    t.Start();
}

私のボックスでは、このコードは毎分0.02秒以内に一貫して刻みます:

2010-01-15T16:42:00.0040001-05:00
2010-01-15T16:43:00.0014318-05:00
2010-01-15T16:44:00.0128643-05:00
2010-01-15T16:45:00.0132961-05:00
51
Jared

どうですか:

int startin = 60 - DateTime.Now.Second;
var t = new System.Threading.Timer(o => Console.WriteLine("Hello"), 
     null, startin * 1000, 60000);
29
aquinas

1秒ごとに起動する Timer コントロールを作成すると(通常は単純なチェック以外は何もしません)、negligibleアプリケーションのオーバーヘッド。

Environment.TickCount または DateTime.Now 最後に保存された時刻(前回の「分刻み」)までであり、合理的に正確な解決策が必要です。これら2つの時間値の分解能は約15ミリ秒であり、これは目的には十分なはずです。

ただし、Timerコントロールの間隔は保証されていません。 Windowsメッセージループ。UIの応答性と結びついています。適度に正確なタイミングでもこれに依存しないでください-ただし、より敏感な方法を使用して時間を確認できる繰り返しイベントを起動するには十分です上記の2つのうちの1つ。

8
Noldorin

あなたはあなたのためにタイマーに関連する多くの問題(時計の変更、アプリの休止状態など)を処理する事後対応​​型の拡張機能でこれを釘付けにすることができます。 NugetパッケージRx-Mainと次のようなコードを使用します。

Action work = () => Console.WriteLine(DateTime.Now.ToLongTimeString());

Scheduler.Default.Schedule(
    // start in so many seconds
    TimeSpan.FromSeconds(60 - DateTime.Now.Second), 
    // then run every minute
    () => Scheduler.Default.SchedulePeriodic(TimeSpan.FromMinutes(1), work));               

Console.WriteLine("Press return.");
Console.ReadLine();

ここで(「ISchedulerPeriodicの紹介」を検索して)対処しているすべての問題を確認してください。 http://blogs.msdn.com/b/rxteam/archive/2012/06/20/reactive-extensions -v2-0-release-candidate-available-now.aspx

7
James World

私はjsutはWPF DispatcherTimerを使用してこのクラスを作成しましたが、スリープ状態から復帰したときに変更をサポートする任意のタイマーのディスパッチャーを交換できます。

クラスは固定の時間ステップで構成され、開始/停止/リセットをサポートします。開始/停止/開始は再開操作のように機能します。その点で、タイマーはストップウォッチのようなものです。

クロックの実装では、1秒間隔でクラスを作成し、イベントをリッスンします。ただし、これはリアルタイムクロックであることに注意してください。ティックイベントが終了する間隔よりも長くかかる場合、クロックがリアルタイムに追いつき、ティックイベントのバーストが発生することに気付くでしょう。

public class FixedStepDispatcherTimer
{
    /// <summary>
    /// Occurs when the timer interval has elapsed.
    /// </summary>
    public event EventHandler Tick;

    DispatcherTimer timer;

    public bool IsRunning { get { return timer.IsEnabled; } }

    long step, nextTick, n;

    public TimeSpan Elapsed { get { return new TimeSpan(n * step); } }

    public FixedStepDispatcherTimer(TimeSpan interval)
    {
        if (interval < TimeSpan.Zero)
        {
            throw new ArgumentOutOfRangeException("interval");
        }
        this.timer = new DispatcherTimer();
        this.timer.Tick += new EventHandler(OnTimerTick);
        this.step = interval.Ticks;
    }

    TimeSpan GetTimerInterval()
    {
        var interval = nextTick - DateTime.Now.Ticks;
        if (interval > 0)
        {
            return new TimeSpan(interval);
        }
        return TimeSpan.Zero; // yield
    }

    void OnTimerTick(object sender, EventArgs e)
    {
        if (DateTime.Now.Ticks >= nextTick)
        {
            n++;
            if (Tick != null)
            {
                Tick(this, EventArgs.Empty);
            }
            nextTick += step;
        }
        var interval = GetTimerInterval();
        Trace.WriteLine(interval);
        timer.Interval = interval;
    }

    public void Reset()
    {
        n = 0;
        nextTick = 0;
    }

    public void Start()
    {
        var now = DateTime.Now.Ticks;
        nextTick = now + (step - (nextTick % step));
        timer.Interval = GetTimerInterval();
        timer.Start();
    }

    public void Stop()
    {
        timer.Stop();
        nextTick = DateTime.Now.Ticks % step;
    }
}
5
John Leidegren

メソッドを作成するか、タイマーを開始する場所にこのコードを配置します。

 int time = 60 - DateTime.Now.Second; // Gets seconds to next minute
        refreshTimer.Interval = time * 1000;
        refreshTimer.Start();

そして、ティックイベントで間隔を60000に設定します。

  private void refreshTimer_Tick(object sender, EventArgs e)
    {
        refreshTimer.Interval = 60000; // Sets interval to 60 seconds
        // Insert Refresh logic
    }
3
Ste Brown

1分に1回、分が変更されたかどうかを確認するために少しのコードを実行すると、CPU時間をそれほど必要とせず、許容できるはずです。

2
Walt W

Quartz.NET についてはどうですか?私はそれが時限アクションを行うには良いフレームワークだと思います。

2
Jehof

ReactiveExtensionsを利用することで、コンソールへの印刷と同じくらい簡単なことをしたい場合は、次のコードを使用できます。

using System;
using System.Reactive.Linq;
namespace ConsoleApplicationExample
{
    class Program
    {
        static void Main()
        {
            Observable.Interval(TimeSpan.FromMinutes(1))
            .Subscribe(_ =>
            {                   
                Console.WriteLine(DateTime.Now.ToString());
            });
            Console.WriteLine(DateTime.Now.ToString()); 
            Console.ReadLine();
        }
    }
}
2
Mdev

または、スリープして、タイムアウトになるまで実行を一時停止することもできます。これは、スリープが終了したときにのみコンピューターをウェイクアップするので、CPU時間を節約し、処理イベント間でCPUの電源を切ることができます。

これには、タイムアウトを修正してドリフトしないという利点があります。

int timeout = 0;

while (true)  {
  timeout = (60 - DateTime.Now.Seconds) * 1000 - DateTime.Now.Millisecond;
  Thread.Sleep(timeout);

  // do your stuff here
}
1
Ron Warholic

2つのタイマーを設定できます。最初の短い間隔のタイマー(1秒ごとに起動する可能性がありますが、2番目のタイマーがその分に起動する精度に依存します)。

メインインターバルタイマーの希望する開始時刻に達するまで、ショートインターバルタイマーを起動します。最初の時間に達すると、2番目のメインインターバルタイマーをアクティブにでき、ショートインターバルタイマーを非アクティブにできます。

void StartTimer()
{

  shortIntervalTimer.Interval = 1000;
  mainIntervalTimer.Interval = 60000; 

  shortIntervalTimer.Tick += 
    new System.EventHandler(this.shortIntervalTimer_Tick);
  mainIntervalTimer.Tick += 
    new System.EventHandler(mainIntervalTimer_Tick);

  shortIntervalTimer.Start();

}

private void shortIntervalTimer_Tick(object sender, System.EventArgs e)
{
  if (DateTime.Now.Second == 0)
    {
      mainIntervalTimer.Start();
      shortIntervalTimer.Stop();
    }
}

private void mainIntervalTimer_Tick(object sender, System.EventArgs e)
{
  // do what you need here //
}
1
Jayden

タイマーセットを使用して毎秒(または精度のしきい値が何であれミリ秒)実行し、現在の時刻が "on the minute"ポイントを超えてそのしきい値内にある場合にのみ機能を実行するメソッドをコーディングします。

0
Charles Bretana

スケジュールされたタスクに使用しているのは、System.Threading.Timer(System.Threading.TimerCallback、object、int、int)であり、コールバックはミリ秒単位で提供される間隔に基づいて実行するコードに設定されています期間値。

0
Brian Surowiec

アクィナスの答えと「ポーリング」の組み合わせについてはどうですか:(言語の混合に対する謝罪)

def waitForNearlyAMinute:
    secsNow = DateTime.Now.Second;
    waitFor = 55 - secsNow;
    setupTimer(waitFor, pollForMinuteEdge)

def pollForMinuteEdge:
    if (DateTime.Now.Second == 0):
        print "Hello, World!";
        waitForNearlyAMinute();
    else:
        setupTimer(0.5, pollForMinuteEdge)
0
Greg