前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅谈 .NET 与 Qt Timer 实现

浅谈 .NET 与 Qt Timer 实现

作者头像
Harper
发布2021-07-27 10:18:36
1.5K3
发布2021-07-27 10:18:36
举报
文章被收录于专栏:Harper的碎碎念Harper的碎碎念

前两天刚好跟同学提起如何实现一个 Timer 。提到了 Kafka 的时间轮和 Go 语言的四叉堆实现。所以就看了下 .NET 是如何实现 Timer 的。

.NET Timer 分为两种,一种是 System.Windows.Threading.DispatcherTimer , 另外一种是System.Timers.Timer

System.Windows.Threading.DispatcherTimer

.NET Framework 相关源码路径:

  • System\Windows\Threading\DispatcherTimer.cs
  • System\Windows\Threading\Dispatcher.cs
  • System\Windows\Threading\DispatcherOperation.cs

简要实现原理:在每次新增 DispatcherTimer 的时候,都会将回调的委托存入 Dispatcher 中的 DispatcherOperation 优先队列里,但是优先级是最差的。然后将 Timer 本身存入当前 Dispatcher 的 Timer List 中。还有一个值得关注的是,时间间隔会加上系统运行时间 Environment.TickCount ,变成绝对时间保存下来,这是为了后边 WM_TIMER 到达之后,对比是否超时做准备。接下来就要关注 Dispatcher 了,当 Dispatcher 新增、删除、响应 Timer 事件以及 DispatcherTimer 调整时间间隔的时候,会调用 UpdateWin32Timer() , 这个方法会在当前 Dispatcher 的 Timer List 中检索最近要触发的 DispatcherTimer,如果当前没有调用过 SetTimer() 或者调用过的 SetTimer 时间间隔比当前最近要触发的长,就取时间间隔,调用 SetTimer()。当收到 WM_TIMER 消息之后,将根据程序运行时间,对比时间间隔,选出已经超时的 Timer,将之前提到的 DispatcherOperation 优先级提升,等到下一个消息循环来到时,回调 Operation 将会被从优先对列取出,并执行。

System.Timers.Timer

.NET Framework 相关源码路径:

  • services\timers\system\timers\Timer.cs
  • system\threading\timer.cs
  • coreclr\src\vm\comthreadpool.cpp

简要实现原理:System.Timers.Timer 只是对 System.Threading.Timer 包装,所以实现上看 System.Threading.Timer 就好。这就不得不提到 System.Threading.Timer 中的 TimerQueue 。 这是存有 TimerQueueTimer 的双向队列。每增加一个 Timer 的时候,都会将一个 TimerQueueTimer 放入 TimerQueue 队列。同时调用运行时的 Native 的代码 AppDomainTimerNative::CreateAppDomainTimer() 。后边就是 Native 的代码逻辑了,具体细节不表了,简单理解就是在线程池中搞一个线程,在线程中调用 SleepEx() 阻塞线程,当线程走完之后触发回调,再调回 .NET 托管代码,找到 TimerQueueTimer ,再执行用户回调。

QTimer

相关源码路径:

  • qtbase\src\corelib\kernel\qeventdispatcher_win.cpp
  • qtbase\src\corelib\kernel\qtimer.cpp
  • qtbase\src\corelib\kernel\qobject.cpp

QTimer 的实现就比较简单了,当增加一个 QTimer 的时候,会在 QEventDispatcher 中调用 Win32 API,同时在 QObject 中将 TimerId 保存到 Vector 中。唯一的细节是,时间间隔在 20ms 以下或者指定 QTimerType 为 Qt::PreciseTimer 的 QTimer 会在底层调用 timeSetEvent() (源码注释中也提到了,虽然方法废弃了,但是精度还是高依旧使用),而其他的就调用 SetTimer() 方法。

谈谈 SetTimer

SetTimer() 的调用是有限制的。不管别人信不信,反正我是信了。这一点在 MSDN 中 SetTimer 的描述并没有,不过通过一些现象,以及网上的一些其他帖子可以得到认证。据 SO 上的一位吃瓜网友表示,SetTimer() 会创建用户对象(虽然这一点微软也没说过),而用户对象在系统中是有限制的(这一点是微软明确说过的),而用户对象的数量上限是在注册表中的,根据微软的文档指示应该是在: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\USERProcessHandleQuota 我看了一下 x64 系统应该是在 HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\Windows\USERProcessHandleQuota 。默认数量是 10000 。

小结

分析过以上几种 Timer 的实现,就知道 .NET 的 Timer 还是做了一些微小的优化的。这也是为什么我跟同事说, 即使都是拿来做 Windows 桌面开发,.NET 框架的上限还是要比 Qt 高的原因。这大概是因为 .NET 本身从一开始就不是以桌面开发作为目标的,所以它更要考虑性能问题,但正因为如此,源码看起来比 Qt 就更为困难;而 Qt 这么实现,对一般的桌面应用来说,完全够用,代码也更容易看懂。虽然两者的实现在极端情况下都会拉闸,但是显然 Qt 的 Timer 实现会更快拉闸……

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-08-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • System.Windows.Threading.DispatcherTimer
  • System.Timers.Timer
  • QTimer
  • 谈谈 SetTimer
  • 小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档