vs mono linux,C# Timer resolution: Linux (mono, dotnet core) vs Windows

问题

I need a timer that fires every 25ms. I've been comparing the default Timer implementation between Windows 10 and Linux (Ubuntu Server 16.10 and 12.04) on both the dotnet core runtime and the latest mono-runtime.

There are some differences in the timer precision that I don't quite understand.

I'm using the following piece of code to test the Timer:

// inside Main()

var s = new Stopwatch();

var offsets = new List();

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)));

On windows it's all over the place:

...

36

25

36

26

36

5,8875 #

Using dotnet core on linux, it's less all over the place:

...

25

30

27

28

27

2.59776536312849 #

But the mono Timer is very precise:

...

25

25

24

25

25

25

0.33 #

Edit: Even on windows, mono still maintains its timing precision:

...

25

25

25

25

25

25

25

24

0.31

What is causing this difference? Is there a benefit to the way the dotnet core runtime does things compared to mono, that justifies the lost precision?

回答1:

Unfortunately you cannot rely on timers in the .NET framework. The best one has 15 ms frequency even if you want to trigger it in every millisecond. But you can implement a high-resolution timer with microsec precision, too.

Note: This works only when Stopwatch.IsHighResolution returns true. In Windows this is true starting with Windows XP; however, I did not test other frameworks.

public class HiResTimer

{

// The number of ticks per one millisecond.

private static readonly float tickFrequency = 1000f / Stopwatch.Frequency;

public event EventHandler 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;

}

}

来源:https://stackoverflow.com/questions/41696144/c-sharp-timer-resolution-linux-mono-dotnet-core-vs-windows

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值