I have a thread to generate a network packet every 40 ms (25 Hz), it's an infinite loop (until told to stop), and I'm using thread.sleep.
When I build the packet, one of the values is the current GPS time, using DateTime.UtcNow
and adding the leap seconds.
This works fine when I start, but it drifts with time, about 2 hours later, it's 5 seconds behind.
I have a Symmetrom GPS Time Server and I'm using their software as the NTP client, and it says the cumulative drift on the PC is about 1.2 seconds (most of that I've noticed is drift while the PC is off and not syncing to NTP).
Anyone have any idea whats going wrong? I know thread.sleep isn't perfect timing, and Windows isn't an RTOS, but the drift doesn't make sense, dropping frames would.
I can't post code due to some proprietary and ITAR issues, but I can post a rough outline:
while(!abort) {
currentTime = DateTime.UtcNow + leapSeconds ;
buildPacket(currentTime);
stream.Write(msg, 0, sendSize);
//NetworkStream Thread.Sleep(40);
}
I'm in Windows 7 and using Visual Studios 2010.
I think this happens because the time that a while loop executes is 40 ms (your sleep) + the time necessary to execute the code that builds the packet.
Have you tried using a System.Threading.Timer ? This way your code will execute in a separate thread then the one that is counting your time. However, I don't think the performance is good enough to keep your real time application running for long.
There is probably many overhead involved, including network IO. You could decouple the timing from the creation like this:
public void Timing()
{
// while (true) to simplify...
// You should probably remember the last time sent and adjust the 40ms accordingly
while (true)
{
SendPacketAsync(DateTime.UtcNow);
Thread.Sleep(40);
}
}
public Task SendPacketAsync(DateTime timing)
{
return Task.Factory.StartNew(() => {
var packet = ...; // use timing
packet.Send(); // abstracted away, probably IO blocking
});
}
The other answers are on to the problem... You have overhead on top of the fact that you are sleeping.
TPL
If you operate in the world of TPL
, then you can create a pretty simple solution:
while(running)
await Task.WhenAll(Task.Delay(40), Task.Run(()=>DoIO()));
This is a great solution because it will wait for the IO operation ( DoIO()
) in case it takes longer than 40ms. It also avoids using Thread.Sleep()
which is always ideal...
Timer
So instead, use a timer ( System.Threading.Timer
) that will fire every 40ms. This way, you can be building and sending the packet, but the timer is also still counting. The risk here is if the IO operation takes longer than 40ms, you have a race condition.
NOTE
40ms is an OK time to expect an accurate callback, HOWEVER, lets say you decided you needed 4ms, then the OS probably wouldn't be able to provide this kind of resolution. You would need a real-time OS for this kind of accuracy.
I would guess you are being bitten by two things.
Here is some skeletal code:
static void MyFunction()
{
//
// Use timeGetDevCaps and timeBeginPeriod to set the system timer
// resolution as close to 1 ms as it will let you.
//
var nextTime = DateTime.UtcNow;
while (!abort)
{
// Send your message, preferably do it asynchronously.
nextTime = nextTime.AddMilliseconds(40);
var sleepInterval = nextTime - DateTime.UtcNow;
// may want to check to make sure sleepInterval is positive.
Thread.Sleep(sleepInterval);
}
//
// Use timeEndPeriod to restore system timer resolution.
//
}
I don't know of any .Net wrappers for the multimedia time* API functions. You will probably need to use PInvoke to call them from C#.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.