Knowing well that Windows is not a real time OS I need to call a function cyclically in a very short time-span below 1 millisecond (Requirement). To avoid a higher CPU load, I felt good about using a System.Threading.Timer
and a TimerCallback
instead of using Thread.Sleep
in an infinitiy-loop. This actually works fine in case the cycle-time is 1ms and above. However, going just 1 microsecond below leads to the fact that the TimerCallback
is called only once.
I have here an example Code where this phenomena is reproducible.
Working example - cyclically with time-span of 1ms ~ 10000 ticks.
Setting a breakpoint at the TimerCallBack
function one can see that it is called cyclically.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Timer timer = new Timer( new TimerCallback(TimerCallBack), null, TimeSpan.Zero, new TimeSpan(10000));
Console.Read();
timer.Dispose();
}
public static void TimerCallBack(object o)
{
Console.Write(".");
}
}
}
In contrast the following issue-example. Setting the breakpoint at the TimerCallBack
function it is clear to see, that it is called only once. But why?
Issue example - time-span of 9999 ticks and callback is for whatever reason only called once.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Timer timer = new Timer( new TimerCallback(TimerCallBack), null, TimeSpan.Zero, new TimeSpan(9999));
Console.Read();
timer.Dispose();
}
public static void TimerCallBack(object o)
{
Console.Write(".");
}
}
}
How does it come to this situation with a value of 9999 so that the callback is not called cyclically anymore?
Is there a better solution or workaround?
.NET Framework 4.7.2
CodePudding user response:
I was able to reproduce your observations. Here is the source code (simplified) of the System.Threading.Timer
constructor that has two TimeSpan
parameters:
public Timer(TimerCallback callback,
object? state,
TimeSpan dueTime,
TimeSpan period)
{
long dueTm = (long)dueTime.TotalMilliseconds;
long periodTm = (long)period.TotalMilliseconds;
TimerSetup(callback, state, (uint)dueTm, (uint)periodTm);
}
As you can see, only the milliseconds of the TimeSpan
s are used. Any finer detail is rounded down to the nearest integer millisecond, which in your case is zero. And according to the documentation:
If
period
is zero (0) or negative one (-1) milliseconds anddueTime
is positive,callback
is invoked once; the periodic behavior of the timer is disabled, but can be re-enabled using theChange
method.
You could consider clicking the "Edit this document" button near the top of the documentation page (it has the icon of a pencil), add a few words to clarify this behavior, and submit a PR to the dotnet/dotnet-api-docs repository.