我有一个线程每40毫秒(25赫兹)生成一个网络数据包,它是一个无限循环(直到被告知停止),我正在使用thread.sleep。
当我构建数据包时,其中一个值是当前的GPS时间,使用DateTime.UtcNow
并添加闰秒。
当我开始时这种方法很好,但它会随着时间的推移而漂移,大约2小时后,它会落后5秒。
我有一台Symmetrom GPS时间服务器,我正在使用他们的软件作为NTP客户端,它说PC上的累积漂移大约是1.2秒(我注意到的大部分是在PC关闭时漂移并且没有同步到NTP)。
任何人都知道什么是错的?我知道thread.sleep并不是完美的计时,而且Windows不是RTOS,但漂移没有意义,丢帧也会。
由于某些专有和ITAR问题,我无法发布代码,但我可以发布一个粗略的大纲:
while(!abort) {
currentTime = DateTime.UtcNow + leapSeconds ;
buildPacket(currentTime);
stream.Write(msg, 0, sendSize);
//NetworkStream Thread.Sleep(40);
}
我在Windows 7中并使用Visual Studios 2010。
答案 0 :(得分:1)
我认为这是因为while循环执行的时间是40 ms(您的睡眠)+执行构建数据包的代码所需的时间。
您是否尝试过使用System.Threading.Timer?这样,您的代码将在一个单独的线程中执行,然后计算您的时间。但是,我认为性能不足以让您的实时应用程序长时间运行。
答案 1 :(得分:0)
可能涉及很多开销,包括网络IO。您可以将创建时间与创建分开:
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
});
}
答案 2 :(得分:0)
其他答案都是针对这个问题的......除了你正在睡觉的事实之外,你还有开销。
<强> TPL 强>
如果您在TPL
的世界中运营,那么您可以创建一个非常简单的解决方案:
while(running)
await Task.WhenAll(Task.Delay(40), Task.Run(()=>DoIO()));
这是一个很好的解决方案,因为它将等待IO操作(DoIO()
),以防它需要超过40毫秒。它也避免使用Thread.Sleep()
,这总是理想的......
<强>定时器强>
相反,请使用每40ms触发一次的计时器(System.Threading.Timer
)。这样,您可以构建和发送数据包,但计时器仍在计数。这里的风险是,如果IO操作时间超过40毫秒,则表示您处于竞争状态。
注意强>
40ms是一个期待准确回调的好时机,但是,假设您认为您需要4ms,那么操作系统可能无法提供这种分辨率。你需要一个实时操作系统来达到这种准确性。
答案 3 :(得分:0)
我猜你有两件事被咬了。
这是一些骨架代码:
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.
//
}
我不知道有关多媒体时间* API函数的任何.Net包装器。您可能需要使用PInvoke从C#中调用它们。