[英]Mono c# TextRenderingHint.SingleBitPerPixelGridFit Windows vs. Linux
[英]C# Timer resolution: Linux (mono, dotnet core) vs Windows
我需要一個每25毫秒觸發一次的計時器。 我一直在比較dotnet核心運行時和最新的單運行時在Windows 10和Linux(Ubuntu Server 16.10和12.04)之間的默認Timer
實現。
我不太了解計時器精度方面的一些差異。
我正在使用以下代碼來測試Timer:
// inside Main()
var s = new Stopwatch();
var offsets = new List<long>();
const int interval = 25;
using (var t = new Timer((obj) =>
{
offsets.Add(s.ElapsedMilliseconds);
s.Restart();
}, null, 0, interval))
{
s.Start();
Thread.Sleep(5000);
}
foreach(var n in offsets)
{
Console.WriteLine(n);
}
Console.WriteLine(offsets.Average(n => Math.Abs(interval - n)));
在Windows上到處都是:
...
36
25
36
26
36
5,8875 # <-- average timing error
在Linux上使用dotnet core,到處都是:
...
25
30
27
28
27
2.59776536312849 # <-- average timing error
但是mono Timer
非常精確:
...
25
25
24
25
25
25
0.33 # <-- average timing error
編輯:即使在Windows上,單聲道仍保持其定時精度:
...
25
25
25
25
25
25
25
24
0.31
是什么造成了這種差異? 與mono相比,dotnet核心運行時做事的方式是否有好處,可以證明喪失精度?
不幸的是,您不能依賴.NET框架中的計時器。 最好的頻率為15毫秒,即使您想每毫秒觸發一次。 但是,您也可以實現具有微秒精度的高分辨率計時器。
注意:僅當Stopwatch.IsHighResolution
返回true時,此方法才有效。 在Windows中,從Windows XP開始是正確的。 但是,我沒有測試其他框架。
public class HiResTimer
{
// The number of ticks per one millisecond.
private static readonly float tickFrequency = 1000f / Stopwatch.Frequency;
public event EventHandler<HiResTimerElapsedEventArgs> Elapsed;
private volatile float interval;
private volatile bool isRunning;
public HiResTimer() : this(1f)
{
}
public HiResTimer(float interval)
{
if (interval < 0f || Single.IsNaN(interval))
throw new ArgumentOutOfRangeException(nameof(interval));
this.interval = interval;
}
// The interval in milliseconds. Fractions are allowed so 0.001 is one microsecond.
public float Interval
{
get { return interval; }
set
{
if (value < 0f || Single.IsNaN(value))
throw new ArgumentOutOfRangeException(nameof(value));
interval = value;
}
}
public bool Enabled
{
set
{
if (value)
Start();
else
Stop();
}
get { return isRunning; }
}
public void Start()
{
if (isRunning)
return;
isRunning = true;
Thread thread = new Thread(ExecuteTimer);
thread.Priority = ThreadPriority.Highest;
thread.Start();
}
public void Stop()
{
isRunning = false;
}
private void ExecuteTimer()
{
float nextTrigger = 0f;
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
while (isRunning)
{
nextTrigger += interval;
float elapsed;
while (true)
{
elapsed = ElapsedHiRes(stopwatch);
float diff = nextTrigger - elapsed;
if (diff <= 0f)
break;
if (diff < 1f)
Thread.SpinWait(10);
else if (diff < 5f)
Thread.SpinWait(100);
else if (diff < 15f)
Thread.Sleep(1);
else
Thread.Sleep(10);
if (!isRunning)
return;
}
float delay = elapsed - nextTrigger;
Elapsed?.Invoke(this, new HiResTimerElapsedEventArgs(delay));
// restarting the timer in every hour to prevent precision problems
if (stopwatch.Elapsed.TotalHours >= 1d)
{
stopwatch.Restart();
nextTrigger = 0f;
}
}
stopwatch.Stop();
}
private static float ElapsedHiRes(Stopwatch stopwatch)
{
return stopwatch.ElapsedTicks * tickFrequency;
}
}
public class HiResTimerElapsedEventArgs : EventArgs
{
public float Delay { get; }
internal HiResTimerElapsedEventArgs(float delay)
{
Delay = delay;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.