I have created an pausable timer, using both a System.Timers.Timer
and a System.Diagnostics.Stopwatch
, as follows:
public class PausableTimer
{
private double interval;
private readonly Timer timer;
private readonly Stopwatch stopwatch;
public event ElapsedEventHandler Tick
{
add => timer.Elapsed += value;
remove => timer.Elapsed -= value;
}
public int Interval
{
get => Convert.ToInt32(timer.Interval);
set
{
if (Enabled)
{
throw new Exception("Can't set interval while running");
}
else
{
interval = Convert.ToDouble(value);
SetInterval(value);
}
}
}
public bool Enabled
{
get => timer.Enabled;
set
{
if (value)
{
Start();
}
else
{
Stop();
}
}
}
public double Elapsed
{
get => stopwatch.Elapsed.TotalMilliseconds;
}
public PausableTimer(int milliseconds)
{
interval = Convert.ToInt32(milliseconds);
stopwatch = new Stopwatch();
timer = new Timer(interval);
timer.AutoReset = true;
timer.Elapsed += OnElapsed;
}
private void OnElapsed(object sender, ElapsedEventArgs e)
{
lock (this)
{
stopwatch.Reset();
SetInterval(interval);
stopwatch.Start();
}
}
public void Start()
{
lock (this)
{
stopwatch.Reset();
timer.Start();
stopwatch.Start();
}
}
public void Stop()
{
lock (this)
{
timer.Stop();
stopwatch.Stop();
}
}
public void Pause()
{
lock (this)
{
Stop();
SetInterval(Interval - stopwatch.Elapsed.TotalMilliseconds);
}
}
private void SetInterval(double value)
{
timer.Interval = Math.Max(value, 0.1);
}
}
Everything seemed just right, untill I started writing a few test cases and found out that the following code reports weird results:
new TaskFactory().StartNew(() =>
{
var stopwatch = new Stopwatch();
var interval = 32;
stopwatch.Start();
Thread.Sleep(interval);
stopwatch.Stop();
Console.WriteLine("Elapsed: {0}. Expected: {1}.", stopwatch.Elapsed.TotalMilliseconds, interval);
});
The lastest 5 results are (stopwatch.Elapsed.TotalMilliseconds
):
- 57.1516
- 57.4105
- 60.5827
- 43.0608
- 52.7358
That's a huge difference. Why aren't these values close to 32
?
This is affecting my PausableTimer
class results.