タイマーオブジェクトがあります。毎分実行したいです。具体的には、OnCallBack
メソッドを実行し、OnCallBack
メソッドの実行中は非アクティブになります。 OnCallBack
メソッドが終了すると、OnCallBack
メソッドはタイマーを再起動します。
ここに私が今持っているものがあります:
private static Timer timer;
private static void Main()
{
timer = new Timer(_ => OnCallBack(), null, 0, 1000 * 10); //every 10 seconds
Console.ReadLine();
}
private static void OnCallBack()
{
timer.Change(Timeout.Infinite, Timeout.Infinite); //stops the timer
Thread.Sleep(3000); //doing some long operation
timer.Change(0, 1000 * 10); //restarts the timer
}
ただし、機能していないようです。 3秒ごとに非常に高速に実行されます。ピリオド(1000 * 10)を上げても。 1000 * 10
に目をつぶっているようです
私は何を間違えましたか?
これはSystem.Threading.Timerの正しい使用法ではありません。タイマーをインスタンス化するとき、ほとんど常に次のことを行う必要があります。
_timer = new Timer( Callback, null, TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite );
これは、間隔が経過したときに一度だけティックするようにタイマーに指示します。次に、コールバック関数で、前ではなく作業が完了したらタイマーを変更します。例:
private void Callback( Object state )
{
// Long running operation
_timer.Change( TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite );
}
したがって、並行性がないため、ロックメカニズムは必要ありません。タイマーは、次の間隔が経過した後、次のコールバック+長時間実行される操作の時間を起動します。
正確にNミリ秒でタイマーを実行する必要がある場合は、ストップウォッチを使用して長時間実行されている操作の時間を測定し、Changeメソッドを適切に呼び出すことをお勧めします。
private void Callback( Object state )
{
Stopwatch watch = new Stopwatch();
watch.Start();
// Long running operation
_timer.Change( Math.Max( 0, TIME_INTERVAL_IN_MILLISECONDS - watch.ElapsedMilliseconds ), Timeout.Infinite );
}
I strongly .NETを実行し、Jeffrey Richterの本を読んでいないCLRを使用している人を励ます-C#経由のCLR、できるだけ早く読む。タイマーとスレッドプールについては、そこで詳しく説明されています。
タイマーを停止する必要はありません、 この投稿の素敵な解決策を参照してください :
「タイマーにコールバックメソッドの起動を続けさせることができますが、Monitor.TryEnter/Exitで非再入可能コードをラップできます。その場合、タイマーを停止/再起動する必要はありません。
private void CreatorLoop(object state)
{
if (Monitor.TryEnter(lockObject))
{
try
{
// Work here
}
finally
{
Monitor.Exit(lockObject);
}
}
}
System.Threading.Timerの使用は必須ですか?
そうでない場合、System.Timers.Timerには便利なStart()およびStop()メソッド(および、AutoResetプロパティをfalseに設定できるため、Stop()は不要で、実行後に単にStart()を呼び出すことができます)。
私はただやる:
private static Timer timer;
private static void Main()
{
timer = new Timer(_ => OnCallBack(), null, 1000 * 10,Timeout.Infinite); //in 10 seconds
Console.ReadLine();
}
private static void OnCallBack()
{
timer.Dispose();
Thread.Sleep(3000); //doing some long operation
timer = new Timer(_ => OnCallBack(), null, 1000 * 10,Timeout.Infinite); //in 10 seconds
}
また、周期パラメータを自分で制御しようとしているため、期間パラメータを無視します。
dueTime
パラメーターに0
を指定し続けるため、元のコードは可能な限り高速で実行されます。 Timer.Change
から:
DueTimeがゼロ(0)の場合、コールバックメソッドがすぐに呼び出されます。
var span = TimeSpan.FromMinutes(2);
var t = Task.Factory.StartNew(async delegate / () =>
{
this.SomeAsync();
await Task.Delay(span, source.Token);
}, source.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
source.Cancel(true/or not);
// or use ThreadPool(whit defaul options thread) like this
Task.Start(()=>{...}), source.Token)
あなたが内部でいくつかのループスレッドを使用したい場合...
public async void RunForestRun(CancellationToken token)
{
var t = await Task.Factory.StartNew(async delegate
{
while (true)
{
await Task.Delay(TimeSpan.FromSeconds(1), token)
.ContinueWith(task => { Console.WriteLine("End delay"); });
this.PrintConsole(1);
}
}, token) // drop thread options to default values;
}
// And somewhere there
source.Cancel();
//or
token.ThrowIfCancellationRequested(); // try/ catch block requred.