.NETにはタイマー的なものがたくさんある。
PeriodicTimer
はそのなかでも非同期タイマーと呼ばれるもの。
WaitForNextTickAsync
メソッドを await する使い方ができる。
// IDisposable using PeriodicTimer timer = new(TimeSpan.FromSeconds(3)); // CancellationToken 対応 CancellationTokenSource cts = new(); try { // 3秒待機を挟みながら5回ループ int count = 0; do { Console.WriteLine(++count); } while (count < 5 && await timer.WaitForNextTickAsync(cts.Token)); // 待機 } catch(OperationCanceledException) { Console.WriteLine("canceled"); }
……このタイマー、Task.Delay
に似てるような?
using PeriodicTimer timer = new(TimeSpan.FromSeconds(3)); await timer.WaitForNextTickAsync(cts.Token); // ↑↓ (?) await Task.Delay(TimeSpan.FromSeconds(3), cts.Token);
他にAPIがあったりするかと思えば、タイマーとしてはWaitForNextTickAsync
がすべてなスタイル。
Dispose()
可能だけれど、中断はTask.Delay
もキャンセルというかたちでサポートしている。
あんまり違わない、のだろうか?
……というのは、自分の浅はかな第一印象。
実際は次のような使い分けがあるらしい。
このAPIは、繰り返し起動するタイマにのみ意味があり、一度だけ起動するタイマはTaskベースになるでしょう。(このために、既にTask.Delayがあります。)
(.NET 6: スレッドの改善 より)
なるほど、タイマーは複数回、Task.Delay
は基本的に一度。
数字的な違いはあるのか、試しに60ミリ秒間隔と1秒間隔の10回ループをBenchmarkDotNetにて較べてみる。
比較用コード折りたたみ
[Params(60d, 1000d)] public double Milliseconds; public TimeSpan TimeSpan; [GlobalSetup] public void Setup() { TimeSpan = TimeSpan.FromMilliseconds(Milliseconds); } [Benchmark] public async Task TaskDelay() { CancellationTokenSource cts = new(); for (int i = 0; i < 10; i++) { await Task.Delay(TimeSpan, cts.Token); } } [Benchmark] public async Task PeriodicTimerWait() { using PeriodicTimer timer = new(TimeSpan); CancellationTokenSource cts = new(); for (int i = 0; i < 10; i++) { await timer.WaitForNextTickAsync(cts.Token); } }
Method | Milliseconds | Mean | Error | StdDev | Allocated | ------------------ |------------- |------------:|----------:|---------:|----------:| TaskDelay | 60 | 622.5 ms | 34.44 ms | 1.89 ms | 3.12 KB | PeriodicTimerWait | 60 | 606.9 ms | 39.60 ms | 2.17 ms | 1.48 KB | TaskDelay | 1000 | 10,140.4 ms | 232.67 ms | 12.75 ms | 3.12 KB | PeriodicTimerWait | 1000 | 10,008.6 ms | 91.02 ms | 4.99 ms | 1.48 KB |
まず、アロケーションがPeriodicTimer
の方が少ない。
そして、Mean が期待される時間に近い。
複数回 await する用途ならPeriodicTimer
が良いと確認。