Given the attached LINQ-Pad snippet.
It creates 8 tasks, executes for 500ms and draws a graph on when the threads were actually running.
On a 4 core CPU it may look like this:
Now, if I add a Thread.Sleep or a Task.Delay within the thread loops, I can visualize the clock of the windows system timer (~15ms):
Now, there's also the timeBeginPeriod function, where I can lower the system timer's resolution (in the example to 1ms). And here's the difference. With Thread.Sleep I get this chart (what I expected):
When using Task.Delay I get the same graph as when the time would be set to 15ms:
Question: Why does the TPL ignore the timer setting?
Here is the code (you need LinqPad 5.28 beta to run the Chart)
void Main()
{
const int Threads = 8;
const int MaxTask = 20;
const int RuntimeMillis = 500;
const int Granularity = 10;
ThreadPool.SetMinThreads(MaxTask, MaxTask);
ThreadPool.SetMaxThreads(MaxTask, MaxTask);
var series = new bool[Threads][];
series.Initialize(i => new bool[RuntimeMillis * Granularity]);
Watch.Start();
var tasks = Async.Tasks(Threads, i => ThreadFunc(series[i], pool));
tasks.Wait();
series.ForAll((x, y) => series[y][x] ? new { X = x / (double)Granularity, Y = y + 1 } : null)
.Chart(i => i.X, i => i.Y, LINQPad.Util.SeriesType.Point)
.Dump();
async Task ThreadFunc(bool[] data, Rendezvous p)
{
double now;
while ((now = Watch.Millis) < RuntimeMillis)
{
await Task.Delay(10);
data[(int)(now * Granularity)] = true;
}
}
}
[DllImport("winmm.dll")] internal static extern uint timeBeginPeriod(uint period);
[DllImport("winmm.dll")] internal static extern uint timeEndPeriod(uint period);
public class Rendezvous
{
private readonly object lockObject = new object();
private readonly int max;
private int n = 0;
private readonly ManualResetEvent waitHandle = new ManualResetEvent(false);
public Rendezvous(int count)
{
this.max = count;
}
public void Join()
{
lock (this.lockObject)
{
if (++this.n >= max)
waitHandle.Set();
}
}
public void Wait()
{
waitHandle.WaitOne();
}
public void Reset()
{
lock (this.lockObject)
{
waitHandle.Reset();
this.n = 0;
}
}
}
public static class ArrayExtensions
{
public static void Initialize<T>(this T[] array, Func<int, T> init)
{
for (int n = 0; n < array.Length; n++)
array[n] = init(n);
}
public static IEnumerable<TReturn> ForAll<TValue, TReturn>(this TValue[][] array, Func<int, int, TReturn> func)
{
for (int y = 0; y < array.Length; y++)
{
for (int x = 0; x < array[y].Length; x++)
{
var result = func(x, y);
if (result != null)
yield return result;
}
}
}
}
public static class Watch
{
private static long start;
public static void Start() => start = Stopwatch.GetTimestamp();
public static double Millis => (Stopwatch.GetTimestamp() - start) * 1000.0 / Stopwatch.Frequency;
public static void DumpMillis() => Millis.Dump();
}
public static class Async
{
public static Task[] Tasks(int tasks, Func<int, Task> thread)
{
return Enumerable.Range(0, tasks)
.Select(i => Task.Run(() => thread(i)))
.ToArray();
}
public static void JoinAll(this Thread[] threads)
{
foreach (var thread in threads)
thread.Join();
}
public static void Wait(this Task[] tasks)
{
Task.WaitAll(tasks);
}
}
timeBeginPeriod() is a legacy function, dates back to Windows 3.1. Microsoft would love to get rid of it, but can't. It has a pretty gross machine-wide side-effect, it increases the clock interrupt rate. The clock interrupt is the "heart-beat" of the OS, it determines when the thread scheduler runs and when sleeping threads can be revived.
The .NET Thread.Sleep() function is not actually implemented by the CLR, it passes the job to the host. Any you'd use to run this test simply delegates the job to the Sleep() winapi function. Which is affected by the clock interrupt rate, as documented in the MSDN article:
To increase the accuracy of the sleep interval, call the timeGetDevCaps function to determine the supported minimum timer resolution and the timeBeginPeriod function to set the timer resolution to its minimum. Use caution when calling timeBeginPeriod, as frequent calls can significantly affect the system clock, system power usage, and the scheduler.
The caution at the end is why Microsoft isn't very happy about it. This does get misused, one of the more egregious cases was noted by one of the founders of this web site in this blog post. Beware of Greeks bearing gifts.
That this changes the accuracy of timers is not exactly a feature. You would not want your program to behave differently just because the user started a browser. So the .NET designers did something about it. Task.Delay() uses System.Threading.Timer under the hood. Instead of just blindly relying on the interrupt rate, it divides the period you specify by 15.6 to calculate the number of time slices. Slightly off from the ideal value btw, which is 15.625, but a side-effect of integer math. So the timer behaves predictably and no longer misbehaves when the clock rate is lowered to 1 msec, it always takes at least one slice. 16 msec in practice since the GetTickCount() unit is milliseconds.
Task.Delay() is implemented via TimerQueueTimer (ref DelayPromise). The resolution of the latter does not change based on timeBeginPeriod(), though this appears to be a recent change in Windows (ref VS Feedback).
Related
This question already has answers here:
High resolution timer in C#
(5 answers)
Closed 3 years ago.
I want to have a timer with about 5millisecond resolution. but the current Timer in .Net has a resolution of about 50ms.
I could not find any working solution that creates a high resolution timer, although some claim that you can do it in C#.
In regards to the information that the OP was specifically asking about the Timer class which fires events at regular intervals. I have amended this answer, with my old answer below the horizontal rule.
I tested the following code with the Timer class, and it seems like it can get at least within the 14 - 15 millisecond range on my machine. Try it out for yourself and see if you can reproduce this. So sub-50 millisecond response times are possible, but it can't get down to exactly one millisecond.
using System;
using System.Timers;
using System.Diagnostics;
public static class Test
{
public static void Main(String[] args)
{
Timer timer = new Timer();
timer.Interval = 1;
timer.Enabled = true;
Stopwatch sw = Stopwatch.StartNew();
long start = 0;
long end = sw.ElapsedMilliseconds;
timer.Elapsed += (o, e) =>
{
start = end;
end = sw.ElapsedMilliseconds;
Console.WriteLine("{0} milliseconds passed", end - start);
};
Console.ReadLine();
}
}
NB: The following is my old answer, when I thought the OP was talking about timing things. The following is merely useful information with respect to timing the duration of things, but doesn't provide any way of firing events at a regular interval. For that purpose, the Timer class is necessary.
Try using the Stopwatch class within System.Diagnostics: http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx
You can query it to check if it's high resolution through it's IsHighResolution field. Also, you can check the exact resolution of the Stopwatch:
int resolution = 1E9 / Stopwatch.Frequency;
Console.WriteLine("The minimum measurable time on this system is: {0} nanoseconds", resolution);
If you're worried about where this is actually sourced, the documentation seems to imply that it actually internally calls the lower level Win32 functions:
The Stopwatch class assists the manipulation of timing-related
performance counters within managed code. Specifically, the Frequency
field and GetTimestamp method can be used in place of the unmanaged
Win32 APIs QueryPerformanceFrequency and QueryPerformanceCounter.
What about this one?
public class HiResTimer
{
private bool isPerfCounterSupported = false;
private Int64 frequency = 0;
// Windows CE native library with QueryPerformanceCounter().
private const string lib = "coredll.dll";
[DllImport(lib)]
private static extern int QueryPerformanceCounter(ref Int64 count);
[DllImport(lib)]
private static extern int QueryPerformanceFrequency(ref Int64 frequency);
public HiResTimer()
{
// Query the high-resolution timer only if it is supported.
// A returned frequency of 1000 typically indicates that it is not
// supported and is emulated by the OS using the same value that is
// returned by Environment.TickCount.
// A return value of 0 indicates that the performance counter is
// not supported.
int returnVal = QueryPerformanceFrequency(ref frequency);
if (returnVal != 0 && frequency != 1000)
{
// The performance counter is supported.
isPerfCounterSupported = true;
}
else
{
// The performance counter is not supported. Use
// Environment.TickCount instead.
frequency = 1000;
}
}
public Int64 Frequency
{
get
{
return frequency;
}
}
public Int64 Value
{
get
{
Int64 tickCount = 0;
if (isPerfCounterSupported)
{
// Get the value here if the counter is supported.
QueryPerformanceCounter(ref tickCount);
return tickCount;
}
else
{
// Otherwise, use Environment.TickCount.
return (Int64)Environment.TickCount;
}
}
}
static void Main()
{
HiResTimer timer = new HiResTimer();
// This example shows how to use the high-resolution counter to
// time an operation.
// Get counter value before the operation starts.
Int64 counterAtStart = timer.Value;
// Perform an operation that takes a measureable amount of time.
for (int count = 0; count < 10000; count++)
{
count++;
count--;
}
// Get counter value when the operation ends.
Int64 counterAtEnd = timer.Value;
// Get time elapsed in tenths of a millisecond.
Int64 timeElapsedInTicks = counterAtEnd - counterAtStart;
Int64 timeElapseInTenthsOfMilliseconds =
(timeElapsedInTicks * 10000) / timer.Frequency;
MessageBox.Show("Time Spent in operation (tenths of ms) "
+ timeElapseInTenthsOfMilliseconds +
"\nCounter Value At Start: " + counterAtStart +
"\nCounter Value At End : " + counterAtEnd +
"\nCounter Frequency : " + timer.Frequency);
}
}
I found a solution to this problem in the following blog:
http://web.archive.org/web/20110910100053/http://www.indigo79.net/archives/27#comment-255
It tells you how to use the multimedia timer to have a timer with high frequency. It is working just fine for me!!!
Here is an implementation based on StopWatch timer
https://gist.github.com/DraTeots/436019368d32007284f8a12f1ba0f545
It works on all platforms and is high precision wherever StopWatch.IsHighPrecision == true
Its Elapsed event is guaranteed to be non overlapping (which might be important to know, because state changes inside the event handler may be left unprotected against multi threaded access)
Here is how to use it:
Console.WriteLine($"IsHighResolution = {HighResolutionTimer.IsHighResolution}");
Console.WriteLine($"Tick time length = {HighResolutionTimer.TickLength} [ms]");
var timer = new HighResolutionTimer(0.5f);
// UseHighPriorityThread = true, sets the execution thread
// to ThreadPriority.Highest. It doesn't provide any precision gain
// in most of the cases and may do things worse for other threads.
// It is suggested to do some studies before leaving it true
timer.UseHighPriorityThread = false;
timer.Elapsed += (s, e) => { /*... e.Delay*/ }; // The call back with real delay info
timer.Start();
timer.Stop(); // by default Stop waits for thread.Join()
// which, if called not from Elapsed subscribers,
// would mean that all Elapsed subscribers
// are finished when the Stop function exits
timer.Stop(joinThread:false) // Use if you don't care and don't want to wait
Here is a benchmark (and a live example):
https://gist.github.com/DraTeots/5f454968ae84122b526651ad2d6ef2a3
The results of setting the timer for 0.5 ms on Windows 10:
It is also worth to mention that:
I had the same precision on mono on Ubuntu.
While playing with the benchmark, the maximum and a very rare deviation I saw was about 0.5 ms
(which probably means nothing, it is not realtime systems, but still worth mentioning)
Stopwatch ticks are not TimeSpan ticks. On that Windows 10 machine
HighResolutionTimer.TickLength is 0.23[ns].
CPU usage of the benchmark is 10% for 0.5ms interval and 0.1% for 200ms interval
Pretty late to the party still might be useful for someone looking for an answer as nothing has been changed for over a decade in the topic.
Background
Any .NET delay instructions would always come down to system clock resolution, the one you set with timeBeginPeriod(). No matter if it's a Thread.Sleep(N), Threading.Timer or Waitable.WaitOne(N). Yet time resolution of DateTime.Now() and System.Diagnostic.Stopwatch is way higher so there is a way of implementing precise timing events known as hot loop. Hot loops yet prone to being threatened badly by OS since those tend to occupy processor core at full. And here's what we do to prevent this:
Surrender thread time quant in a hot loop to other threads once there's no more need for it by calling Thread.Sleep(0) or .WaitOne(0)
Below is a code piece showing simple implementation of high resolution scheduler:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
/// <summary>
/// High resolution scheduler.
/// License: public domain (no restrictions or obligations)
/// Author: Vitaly Vinogradov
/// </summary>
public class HiResScheduler : IDisposable
{
/// <summary>
/// Scheduler would automatically downgrade itself to cold loop (Sleep(1)) when there are no
/// tasks earlier than the treshold.
/// </summary>
public const int HOT_LOOP_TRESHOLD_MS = 16;
protected class Subscriber : IComparable<Subscriber>, IComparable
{
public Action Callback { get; set; }
public double DelayMs { get; set; }
public Subscriber(double delay, Action callback)
{
DelayMs = delay;
Callback = callback;
}
public int CompareTo(Subscriber other)
{
return DelayMs.CompareTo(other.DelayMs);
}
public int CompareTo(object obj)
{
if (ReferenceEquals(obj, null))
return -1;
var other = obj as Subscriber;
if (ReferenceEquals(other, null))
return -1;
return CompareTo(other);
}
}
private Thread _spinner;
private ManualResetEvent _allowed = new ManualResetEvent(false);
private AutoResetEvent _wakeFromColdLoop = new AutoResetEvent(false);
private bool _disposing = false;
private bool _adding = false;
private List<Subscriber> _subscribers = new List<Subscriber>();
private List<Subscriber> _pendingSubscribers = new List<Subscriber>();
public bool IsActive { get { return _allowed.WaitOne(0); } }
public HiResScheduler()
{
_spinner = new Thread(DoSpin);
_spinner.Start();
}
public void Start()
{
_allowed.Set();
}
public void Pause()
{
_allowed.Reset();
}
public void Enqueue(double delayMs, Action callback)
{
lock (_pendingSubscribers)
{
_pendingSubscribers.Add(new Subscriber(delayMs, callback));
_adding = true;
if (delayMs <= HOT_LOOP_TRESHOLD_MS * 2)
_wakeFromColdLoop.Set();
}
}
private void DoSpin(object obj)
{
var sw = new Stopwatch();
sw.Start();
var nextFire = null as Subscriber;
while (!_disposing)
{
_allowed.WaitOne();
if (nextFire != null && sw.Elapsed.TotalMilliseconds >= nextFire?.DelayMs)
{
var diff = sw.Elapsed.TotalMilliseconds;
sw.Restart();
foreach (var item in _subscribers)
item.DelayMs -= diff;
foreach (var item in _subscribers.Where(p => p.DelayMs <= 0).ToList())
{
item.Callback?.Invoke();
_subscribers.Remove(item);
}
nextFire = _subscribers.FirstOrDefault();
}
if (_adding)
lock (_pendingSubscribers)
{
_subscribers.AddRange(_pendingSubscribers);
_pendingSubscribers.Clear();
_subscribers.Sort();
_adding = false;
nextFire = _subscribers.FirstOrDefault();
}
if (nextFire == null || nextFire.DelayMs > HOT_LOOP_TRESHOLD_MS)
_wakeFromColdLoop.WaitOne(1);
else
_wakeFromColdLoop.WaitOne(0);
}
}
public void Dispose()
{
_disposing = true;
}
}
The system clock "ticks" at a constant rate. In order to raise the accuracy of timer dependent function*s, call **timeGetDevCaps* which determines the minimum supported timer resolution.
Then call timeBeginPeriod setting the timer resolution to its minimum.
Caution: By invoking timeBeginPeriod, other timer dependent function can be significantly affected such as the system clock, system power usage, and the scheduler. Thus start your application with timeBeginPeriod and end it with timeEndPeriod
The previous example doesn't work unless the frequency is in milliseconds; the perf timer frequency is rarely in ms.
private static Int64 m_iPerfFrequency = -1;
public static double GetPerfCounter()
{
// see if we need to get the frequency
if (m_iPerfFrequency < 0)
{
if (QueryPerformanceFrequency(out m_iPerfFrequency) == 0)
{
return 0.0;
}
}
Int64 iCount = 0;
if (QueryPerformanceCounter(out iCount) == 0)
{
return 0.0;
}
return (double)iCount / (double)m_iPerfFrequency;
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int QueryPerformanceCounter(out Int64 iCount);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int QueryPerformanceFrequency(out Int64 iFrequency);
This returns the perf counter in seconds. The reason you'd use the perf timer is to share a timer with legacy C++ code, or get a more precise timer than the C# StopWatch class.
You could use QueryPerformanceCounter() and QueryPerformanceTimer() as outlined in this article.
private void RunEveryTenFrames(Color32[] pixels, int width, int height)
{
var thread = new Thread(() =>
{
Perform super = new HeavyOperation();
if (super != null)
{
Debug.Log("Result: " + super);
ResultHandler.handle(super);
}
});
thread.Start();
}
I'm running this function every 10 frames in Unity. Is this a bad idea. Also, when I try to add thread.Abort() inside the thread, it says thread is not defined and can't use local variable before it's defined error.
Is it a good idea to create a new thread every 10 frames in Unity?
No. 10 frames is too small for repeatedly creating new Thread.
Creating new Thread will cause overhead each time. It's not bad when done once in a while. It is when done every 10 frames. Remember this is not every 10 seconds. It is every 10 frames.
Use ThreadPool. By using ThreadPool with ThreadPool.QueueUserWorkItem, you are re-using Thread that already exist in the System in instead of creating new ones each time.
Your new RunEveryTenFrames function with ThreadPool should look something like this:
private void RunEveryTenFrames(Color32[] pixels, int width, int height)
{
//Prepare parameter to send to the ThreadPool
Data data = new Data();
data.pixels = pixels;
data.width = width;
data.height = height;
ThreadPool.QueueUserWorkItem(new WaitCallback(ExtractFile), data);
}
private void ExtractFile(object a)
{
//Retrive the parameters
Data data = (Data)a;
Perform super = new HeavyOperation();
if (super != null)
{
Debug.Log("Result: " + super);
ResultHandler.handle(super);
}
}
public struct Data
{
public Color32[] pixels;
public int width;
public int height;
}
I you ever need to call into Unity's API or use Unity's API from this Thread, see my other post or how to do that.
Update:
I isolated the problem to its core components and a much shorter minimal working example in another question:
Observable.Interval not updating UI with expected frequency
I need to display a sequence of images (which are read from a folder) as a "movie", at a constant, previously known, frame-rate.
I have an Image control in WPF, whose Source is data-bound to a ViewModel property Image. I then proceed to update that image on a timely basis, using an Observable.Interval event source. For each elapsed interval, the UpdateImage is called, which updates the image inside a Task.Run() call.
My problem is: when I experimentally increase effective frame-rate (which depends on actual frame-rate and also on playback speed), the playback keeps being at normal speed up tu a given value. Above that value, it starts to look slower.
I believe that it has to do with RaisePropertyChanged call, but I'm not sure. I tried using SubscribeOnDispatcher (or whas it ObserveOnDispatcher?) but anyway it didnt have an effect.
Questions are:
- What I am doing wrong?
- How could I investigate and resolve the problem?
UPDATE:
It's worth mentioning that the getter for Image calls CreateImageForIndex(), which is a method that could have a non-trivial cost. Would it be possible to "async" it?
ViewModel (partial):
CancellationTokenSource _cancelPlay;
double _speed;
public void Play(double speed)
{
_speed = speed;
_cancelPlay = new CancellationTokenSource();
Observable.Interval(TimeSpan.FromSeconds(1.0 / (Math.Abs(speed) * Exame.Model.Configurações.FrameRate)))
.Subscribe(t => ExecuteRun(), _cancelPlay.Token);
}
public void Stop()
{
_cancelPlay?.Cancel();
}
void ExecuteRun()
{
Task.Run(() =>
{
Index = Math.Max(0, Math.Min(_model.MaxIndex, Index + 1 * Math.Sign(_speed)));
});
}
public int Index
{
get { return _model.Index; }
set
{
_model.Index = value;
RaisePropertyChanged(null); // this tells view to get a new value for `Image` too!
}
}
public ImageSource Image => _model.CreateImageForIndex(Index);
I've followed a hint from #Enigmativity (in the other answer I posted), that in Windows the resolution of timers is 15ms at most.
So I tested another strategy: spinning a while loop with an "when elapsed" condition, similar to a game loop, measuring time with a Stopwatch, and everything is working fine now.
public void Play(double speed)
{
_speed = speed;
_cancelPlay?.Cancel();
_cancelPlay = new CancellationTokenSource();
Task.Run(() => PlayLoop(_cancelPlay.Token), _cancelPlay.Token);
}
void PlayLoop(CancellationToken token)
{
var sw = Stopwatch.StartNew();
double previous = sw.ElapsedMilliseconds;
double timeInterval = 1000.0 / (Math.Abs(_speed) * _exame.Model.Configurações.FrameRate);
while (!token.IsCancellationRequested)
{
var current = sw.ElapsedMilliseconds;
var elapsed = current - previous;
if (elapsed > timeInterval)
{
Índice = Math.Max(0, Math.Min(ÍndiceMáximo, Índice + 1 * Math.Sign(_speed)));
previous = current;
}
}
}
This question already has answers here:
High resolution timer in C#
(5 answers)
Closed 3 years ago.
I want to have a timer with about 5millisecond resolution. but the current Timer in .Net has a resolution of about 50ms.
I could not find any working solution that creates a high resolution timer, although some claim that you can do it in C#.
In regards to the information that the OP was specifically asking about the Timer class which fires events at regular intervals. I have amended this answer, with my old answer below the horizontal rule.
I tested the following code with the Timer class, and it seems like it can get at least within the 14 - 15 millisecond range on my machine. Try it out for yourself and see if you can reproduce this. So sub-50 millisecond response times are possible, but it can't get down to exactly one millisecond.
using System;
using System.Timers;
using System.Diagnostics;
public static class Test
{
public static void Main(String[] args)
{
Timer timer = new Timer();
timer.Interval = 1;
timer.Enabled = true;
Stopwatch sw = Stopwatch.StartNew();
long start = 0;
long end = sw.ElapsedMilliseconds;
timer.Elapsed += (o, e) =>
{
start = end;
end = sw.ElapsedMilliseconds;
Console.WriteLine("{0} milliseconds passed", end - start);
};
Console.ReadLine();
}
}
NB: The following is my old answer, when I thought the OP was talking about timing things. The following is merely useful information with respect to timing the duration of things, but doesn't provide any way of firing events at a regular interval. For that purpose, the Timer class is necessary.
Try using the Stopwatch class within System.Diagnostics: http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx
You can query it to check if it's high resolution through it's IsHighResolution field. Also, you can check the exact resolution of the Stopwatch:
int resolution = 1E9 / Stopwatch.Frequency;
Console.WriteLine("The minimum measurable time on this system is: {0} nanoseconds", resolution);
If you're worried about where this is actually sourced, the documentation seems to imply that it actually internally calls the lower level Win32 functions:
The Stopwatch class assists the manipulation of timing-related
performance counters within managed code. Specifically, the Frequency
field and GetTimestamp method can be used in place of the unmanaged
Win32 APIs QueryPerformanceFrequency and QueryPerformanceCounter.
What about this one?
public class HiResTimer
{
private bool isPerfCounterSupported = false;
private Int64 frequency = 0;
// Windows CE native library with QueryPerformanceCounter().
private const string lib = "coredll.dll";
[DllImport(lib)]
private static extern int QueryPerformanceCounter(ref Int64 count);
[DllImport(lib)]
private static extern int QueryPerformanceFrequency(ref Int64 frequency);
public HiResTimer()
{
// Query the high-resolution timer only if it is supported.
// A returned frequency of 1000 typically indicates that it is not
// supported and is emulated by the OS using the same value that is
// returned by Environment.TickCount.
// A return value of 0 indicates that the performance counter is
// not supported.
int returnVal = QueryPerformanceFrequency(ref frequency);
if (returnVal != 0 && frequency != 1000)
{
// The performance counter is supported.
isPerfCounterSupported = true;
}
else
{
// The performance counter is not supported. Use
// Environment.TickCount instead.
frequency = 1000;
}
}
public Int64 Frequency
{
get
{
return frequency;
}
}
public Int64 Value
{
get
{
Int64 tickCount = 0;
if (isPerfCounterSupported)
{
// Get the value here if the counter is supported.
QueryPerformanceCounter(ref tickCount);
return tickCount;
}
else
{
// Otherwise, use Environment.TickCount.
return (Int64)Environment.TickCount;
}
}
}
static void Main()
{
HiResTimer timer = new HiResTimer();
// This example shows how to use the high-resolution counter to
// time an operation.
// Get counter value before the operation starts.
Int64 counterAtStart = timer.Value;
// Perform an operation that takes a measureable amount of time.
for (int count = 0; count < 10000; count++)
{
count++;
count--;
}
// Get counter value when the operation ends.
Int64 counterAtEnd = timer.Value;
// Get time elapsed in tenths of a millisecond.
Int64 timeElapsedInTicks = counterAtEnd - counterAtStart;
Int64 timeElapseInTenthsOfMilliseconds =
(timeElapsedInTicks * 10000) / timer.Frequency;
MessageBox.Show("Time Spent in operation (tenths of ms) "
+ timeElapseInTenthsOfMilliseconds +
"\nCounter Value At Start: " + counterAtStart +
"\nCounter Value At End : " + counterAtEnd +
"\nCounter Frequency : " + timer.Frequency);
}
}
I found a solution to this problem in the following blog:
http://web.archive.org/web/20110910100053/http://www.indigo79.net/archives/27#comment-255
It tells you how to use the multimedia timer to have a timer with high frequency. It is working just fine for me!!!
Here is an implementation based on StopWatch timer
https://gist.github.com/DraTeots/436019368d32007284f8a12f1ba0f545
It works on all platforms and is high precision wherever StopWatch.IsHighPrecision == true
Its Elapsed event is guaranteed to be non overlapping (which might be important to know, because state changes inside the event handler may be left unprotected against multi threaded access)
Here is how to use it:
Console.WriteLine($"IsHighResolution = {HighResolutionTimer.IsHighResolution}");
Console.WriteLine($"Tick time length = {HighResolutionTimer.TickLength} [ms]");
var timer = new HighResolutionTimer(0.5f);
// UseHighPriorityThread = true, sets the execution thread
// to ThreadPriority.Highest. It doesn't provide any precision gain
// in most of the cases and may do things worse for other threads.
// It is suggested to do some studies before leaving it true
timer.UseHighPriorityThread = false;
timer.Elapsed += (s, e) => { /*... e.Delay*/ }; // The call back with real delay info
timer.Start();
timer.Stop(); // by default Stop waits for thread.Join()
// which, if called not from Elapsed subscribers,
// would mean that all Elapsed subscribers
// are finished when the Stop function exits
timer.Stop(joinThread:false) // Use if you don't care and don't want to wait
Here is a benchmark (and a live example):
https://gist.github.com/DraTeots/5f454968ae84122b526651ad2d6ef2a3
The results of setting the timer for 0.5 ms on Windows 10:
It is also worth to mention that:
I had the same precision on mono on Ubuntu.
While playing with the benchmark, the maximum and a very rare deviation I saw was about 0.5 ms
(which probably means nothing, it is not realtime systems, but still worth mentioning)
Stopwatch ticks are not TimeSpan ticks. On that Windows 10 machine
HighResolutionTimer.TickLength is 0.23[ns].
CPU usage of the benchmark is 10% for 0.5ms interval and 0.1% for 200ms interval
Pretty late to the party still might be useful for someone looking for an answer as nothing has been changed for over a decade in the topic.
Background
Any .NET delay instructions would always come down to system clock resolution, the one you set with timeBeginPeriod(). No matter if it's a Thread.Sleep(N), Threading.Timer or Waitable.WaitOne(N). Yet time resolution of DateTime.Now() and System.Diagnostic.Stopwatch is way higher so there is a way of implementing precise timing events known as hot loop. Hot loops yet prone to being threatened badly by OS since those tend to occupy processor core at full. And here's what we do to prevent this:
Surrender thread time quant in a hot loop to other threads once there's no more need for it by calling Thread.Sleep(0) or .WaitOne(0)
Below is a code piece showing simple implementation of high resolution scheduler:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
/// <summary>
/// High resolution scheduler.
/// License: public domain (no restrictions or obligations)
/// Author: Vitaly Vinogradov
/// </summary>
public class HiResScheduler : IDisposable
{
/// <summary>
/// Scheduler would automatically downgrade itself to cold loop (Sleep(1)) when there are no
/// tasks earlier than the treshold.
/// </summary>
public const int HOT_LOOP_TRESHOLD_MS = 16;
protected class Subscriber : IComparable<Subscriber>, IComparable
{
public Action Callback { get; set; }
public double DelayMs { get; set; }
public Subscriber(double delay, Action callback)
{
DelayMs = delay;
Callback = callback;
}
public int CompareTo(Subscriber other)
{
return DelayMs.CompareTo(other.DelayMs);
}
public int CompareTo(object obj)
{
if (ReferenceEquals(obj, null))
return -1;
var other = obj as Subscriber;
if (ReferenceEquals(other, null))
return -1;
return CompareTo(other);
}
}
private Thread _spinner;
private ManualResetEvent _allowed = new ManualResetEvent(false);
private AutoResetEvent _wakeFromColdLoop = new AutoResetEvent(false);
private bool _disposing = false;
private bool _adding = false;
private List<Subscriber> _subscribers = new List<Subscriber>();
private List<Subscriber> _pendingSubscribers = new List<Subscriber>();
public bool IsActive { get { return _allowed.WaitOne(0); } }
public HiResScheduler()
{
_spinner = new Thread(DoSpin);
_spinner.Start();
}
public void Start()
{
_allowed.Set();
}
public void Pause()
{
_allowed.Reset();
}
public void Enqueue(double delayMs, Action callback)
{
lock (_pendingSubscribers)
{
_pendingSubscribers.Add(new Subscriber(delayMs, callback));
_adding = true;
if (delayMs <= HOT_LOOP_TRESHOLD_MS * 2)
_wakeFromColdLoop.Set();
}
}
private void DoSpin(object obj)
{
var sw = new Stopwatch();
sw.Start();
var nextFire = null as Subscriber;
while (!_disposing)
{
_allowed.WaitOne();
if (nextFire != null && sw.Elapsed.TotalMilliseconds >= nextFire?.DelayMs)
{
var diff = sw.Elapsed.TotalMilliseconds;
sw.Restart();
foreach (var item in _subscribers)
item.DelayMs -= diff;
foreach (var item in _subscribers.Where(p => p.DelayMs <= 0).ToList())
{
item.Callback?.Invoke();
_subscribers.Remove(item);
}
nextFire = _subscribers.FirstOrDefault();
}
if (_adding)
lock (_pendingSubscribers)
{
_subscribers.AddRange(_pendingSubscribers);
_pendingSubscribers.Clear();
_subscribers.Sort();
_adding = false;
nextFire = _subscribers.FirstOrDefault();
}
if (nextFire == null || nextFire.DelayMs > HOT_LOOP_TRESHOLD_MS)
_wakeFromColdLoop.WaitOne(1);
else
_wakeFromColdLoop.WaitOne(0);
}
}
public void Dispose()
{
_disposing = true;
}
}
The system clock "ticks" at a constant rate. In order to raise the accuracy of timer dependent function*s, call **timeGetDevCaps* which determines the minimum supported timer resolution.
Then call timeBeginPeriod setting the timer resolution to its minimum.
Caution: By invoking timeBeginPeriod, other timer dependent function can be significantly affected such as the system clock, system power usage, and the scheduler. Thus start your application with timeBeginPeriod and end it with timeEndPeriod
The previous example doesn't work unless the frequency is in milliseconds; the perf timer frequency is rarely in ms.
private static Int64 m_iPerfFrequency = -1;
public static double GetPerfCounter()
{
// see if we need to get the frequency
if (m_iPerfFrequency < 0)
{
if (QueryPerformanceFrequency(out m_iPerfFrequency) == 0)
{
return 0.0;
}
}
Int64 iCount = 0;
if (QueryPerformanceCounter(out iCount) == 0)
{
return 0.0;
}
return (double)iCount / (double)m_iPerfFrequency;
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int QueryPerformanceCounter(out Int64 iCount);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int QueryPerformanceFrequency(out Int64 iFrequency);
This returns the perf counter in seconds. The reason you'd use the perf timer is to share a timer with legacy C++ code, or get a more precise timer than the C# StopWatch class.
You could use QueryPerformanceCounter() and QueryPerformanceTimer() as outlined in this article.
I am using NAudio to play a sinewave of a given frequency as in the blog post Playback of Sine Wave in NAudio. I just want the sound to play() for x milliseconds and then stop.
I tried a thread.sleep, but the sound stops straightaway. I tried a timer, but when the WaveOut is disposed there is a cross-thread exception.
I tried this code, but when I call beep the program freezes.
public class Beep
{
public Beep(int freq, int ms)
{
SineWaveProvider32 sineWaveProvider = new SineWaveProvider32();
sineWaveProvider.Amplitude = 0.25f;
sineWaveProvider.Frequency = freq;
NAudio.Wave.WaveOut waveOut = new NAudio.Wave.WaveOut(WaveCallbackInfo.FunctionCallback());
waveOut.Init(sineWaveProvider);
waveOut.Play();
Thread.Sleep(ms);
waveOut.Stop();
waveOut.Dispose();
}
}
public class SineWaveProvider32 : NAudio.Wave.WaveProvider32
{
int sample;
public SineWaveProvider32()
{
Frequency = 1000;
Amplitude = 0.25f; // Let's not hurt our ears
}
public float Frequency { get; set; }
public float Amplitude { get; set; }
public override int Read(float[] buffer, int offset, int sampleCount)
{
int sampleRate = WaveFormat.SampleRate;
for (int n = 0; n < sampleCount; n++)
{
buffer[n + offset] = (float)(Amplitude * Math.Sin((2 * Math.PI * sample * Frequency) / sampleRate));
sample++;
if (sample >= sampleRate)
sample = 0;
}
}
The SineWaveProvider32 class doesn't need to indefinitely provide audio. If you want the beep to have a maximum duration of a second (say), then for mono 44.1 kHz, you need to provide 44,100 samples. The Read method should return 0 when it has no more data to supply.
To make your GUI thread not block, you need to get rid of Thread.Sleep, waveOut.Stop and Dispose, and simply start playing the audio (you may find window callbacks more reliable than function).
Then, when the audio has finished playing, you can close and clean up the WaveOut object.
Check out the blog post Pass Variables to a New Thread in C# on how to pass variables to another thread.
I think what you want to do is something like create a thread that plays your sound, create a timer, and start the thread. When the timer expires, kill the thread, and when the thread closes have it do all the cleanup.