using System.Threading.Tasks;
using System.Diagnostics;
public static async Task Main()
using UpdateablePeriodicTimer timer = new(TimeSpan.FromMilliseconds(1));
for (int i = 1; i <= 6; i++)
timer.Period = TimeSpan.FromMilliseconds(i * 100);
Print($"Before await #{i}, Period: {timer.Period.TotalMilliseconds:#,0} msec");
Stopwatch stopwatch = Stopwatch.StartNew();
bool result = await timer.WaitForNextTickAsync();
Print($"After await #{i}, duration: {stopwatch.ElapsedMilliseconds:#,0} msec, result: {result}");
public class UpdateablePeriodicTimer : IDisposable
private readonly object _locker = new();
private PeriodicTimer _timer;
private PeriodicTimer _newTimer;
private TimeSpan _period;
public UpdateablePeriodicTimer(TimeSpan period)
get { lock (_locker) return _period; }
PeriodicTimer timerToDispose;
if (_timer is null) throw new ObjectDisposedException(null,
$"The {nameof(UpdateablePeriodicTimer)} has been disposed.");
timerToDispose = _newTimer;
timerToDispose?.Dispose();
public async ValueTask<bool> WaitForNextTickAsync(
CancellationToken cancellationToken = default)
cancellationToken.ThrowIfCancellationRequested();
ValueTask<bool> waitTask;
if (_timer is null) return false;
if (_waiting) throw new InvalidOperationException();
waitTask = _timer.WaitForNextTickAsync(cancellationToken);
try { return await waitTask.ConfigureAwait(false); }
PeriodicTimer timerToDispose = null;
if (_timer is not null && _newTimer is not null)
timerToDispose?.Dispose();
PeriodicTimer timerToDispose;
if (_timer is null) return;
timerToDispose.Dispose();
private static void Print(object value)
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} [{Thread.CurrentThread.ManagedThreadId}] > {value}");