如何每 100 毫秒执行不超过一次的某些类方法?
Posted
技术标签:
【中文标题】如何每 100 毫秒执行不超过一次的某些类方法?【英文标题】:how to execute certain class method no more than once per 100ms? 【发布时间】:2011-04-15 14:32:22 【问题描述】:我正在编写交易软件,需要对一种方法进行 QoS 处理,该方法的执行频率不应超过每秒 10 次。由于我是 C# 的初学者并且几乎不熟悉库,因此我想仔细检查我的代码是否“最佳”。我正在使用Stopwatch
,因为我不知道 C# 中的任何其他计时器。
Stopwatch updateStopwatch = Stopwatch.StartNew();
private void update()
if (updateStopwatch.ElapsedMilliseconds < 100)
Console.WriteLine("!skip update " + updateStopwatch.ElapsedMilliseconds);
return;
else
Console.WriteLine("!update");
updateStopwatch.Restart();;
// do work here
upd 现在看来,Stopwatch 非常适合这项任务。但是可能它太慢了,如果是这样的话 DateTime 可能会更好。也卖Stopwatch vs. using System.DateTime.Now for timing events
【问题讨论】:
update()
怎么称呼?什么时候触发一些事件?一个按钮被点击?一些计时器触发器?
update() 在事件中调用(当市场数据发生变化时)。有几个事件可以调用 update()
【参考方案1】:
您使用Stopwatch
的技术是防止代码更频繁执行的最佳解决方案。正如其他人所说,如果您想确保按计划执行该方法,使用Timer
是一个更好的解决方案。
任何基于DateTime
的方法都从根本上被破坏了,因为它会在日期更改时失败。这在夏令时切换期间尤其明显。当我们“提前”时,更新可能会快速连续运行两次,因为代码认为距离上一次更新已经过去了一个小时。这还不错。但是当我们“回退”时,更新将被暂停整整一个小时,因为最后一次更新时间提前了一个小时。
如果您的计算机设置为从 NTP 服务器定期更新其时间,则可能会发生同样的事情,尽管没有那么严重。如果时间提前,则有可能快速连续发生两次更新。如果时间倒退,则在时钟倒退的时间内可能不会发生更新。
有一些方法可以解决这个问题(例如使用毫秒数的绝对值),但您只是在损坏的解决方案上贴上绷带。您不应该依赖DateTime
来处理这样的时间间隔,因为您的程序无法控制系统时钟——它可以随时更改。
Stopwatch
是这里唯一合理的解决方案,因为它取决于 CPU 的性能计数器,它只会增加。您不会遇到有人设置计数器的问题,也不会遇到像Environment.TickCount
这样的问题会遇到的翻转问题。
有人认为Stopwatch
会导致性能损失,而DateTime
不会。我的测试表明这是不真实的。
【讨论】:
依赖DateTime
是什么意思?
我没有说某事取决于DateTime
。我说过,如果你写一个依赖DateTime
的解决方案,它会失败。【参考方案2】:
秒表和计时器是相当昂贵的对象。您可以简单地将 DateTime 对象作为变量保存并执行比较。
DateTime lastCheck = DateTime.Now;
private void update()
// DateTime.Subtract returns a TimeSpan
int elapsed = DateTime.Now.Subtract(lastCheck).Milliseconds;
if (elapsed < 100)
Console.WriteLine("!skip update " + elapsed.ToString());
return;
else
Console.WriteLine("!update");
lastCheck = DateTime.Now;
// do work here
【讨论】:
DateTime 也相当昂贵。唯一的区别是您使用主线程而不是主线程的子线程来处理它。 @Ramhound:DateTime 并不像计时器或秒表那样昂贵。对于 DateTime 不需要的事件,必须加载和触发大量事件。从功能上讲,DateTime 可能比理想的更昂贵,但仍然可能更容易维护并且更容易实现。无论如何,这只是一个想法,很可能不是最理想的解决方案。Stopwatch
怎么贵?它所做的只是跟踪开始时间,每次访问Elapsed
属性时,它都会调用QueryPerformanceCounter
。根本不涉及“事件”。
你会想要TotalMilliseconds
,而不是Milliseconds
。
@Jim Mischel:正确的 TotalMilliseconds 会更合适。我面前没有基准。我读了一篇文章(我现在找不到了),指出计时器和秒表的性能受到影响(计时器是最差的)。我不能权威地说话,所以持保留态度。【参考方案3】:
我不会使用Stopwatch
或任何其他Timer
之类的东西。而是只存储方法调用的时间,如果当前时间和存储时间之间的差异大于 100ms,则只执行后续调用。
您可以实现一个帮助类以更通用的方式执行此操作:
public class TimedGate
private DateTime m_Last;
private TimeSpan m_Gap;
public TimedGate(TimeSpan gap)
m_Gap = gap;
public bool TryEnter()
DateTime now = DateTime.UtcNow;
if (now.Subtract(m_Last) > m_Gap)
m_LastEntered = now;
return true;
return false;
像这样使用它:
TimedGate m_UpdateGate = new TimedGate(TimeSpan.FromMilliseconds(100));
private void Update()
if (m_UpdateGate.TryEnter())
Console.WriteLine("!update");
// do work here
else
Console.WriteLine("!skip update");
【讨论】:
【参考方案4】:总是有System.Timer
计时器。
这可能比秒表更容易使用(通常用于测量事情需要多长时间)。
代码:
var timer = new System.Timers.Timer();
// Hook up the Elapsed event for the timer using a lambda
timer.Elapsed += (o, e) => Console.WriteLine("Timer elapsed");
// Set the Interval to 100 ms
timer.Interval = 100;
// Start the timer.
timer.Enabled = true;
MSDN 文档:http://msdn.microsoft.com/en-us/library/system.timers.timer(v=VS.100).aspx
【讨论】:
秒表基于高分辨率计时器。我不认为 System.Timer 基于高分辨率计时器。秒表可以通过IsHighResolution
查看。
无论哪种方式,秒表都是测量时间的,不能用作计时器。并且:“如果安装的硬件和操作系统支持高分辨率性能计数器,则 Stopwatch 类使用该计数器来测量经过的时间。否则,Stopwatch 类使用系统计时器来测量经过的时间。”
其实我不需要计时器,这意味着我不需要每 100 毫秒生成一次事件。我需要防止每 100 毫秒产生一个以上的事件。以上是关于如何每 100 毫秒执行不超过一次的某些类方法?的主要内容,如果未能解决你的问题,请参考以下文章