Windows 服务中计时器的奇怪问题

问题描述 投票:0回答:1

我正在开发一个机器人交易者(通过 API 与经纪人进行自动交易)。
机器人交易者(windows-)客户端每 10 秒通过 API 访问实时数据(查询数据、买入和卖出)。
为了能够进行测试(在 windows robo-trader 中更改代码并以快速、简单的方式查看结果),我还开发了一个 windows 服务,每 10 秒获取一次数据(如客户端),然后将数据存储在 SQL 服务器上,以便我能够使用 SQL 服务器上的数据(带有历史数据)进行模拟。
基本问题是,数据不同步
例如。 Windows 客户端获取 10:02:13 的数据,而 Windows 服务获取 10:02:19 的(相同)数据,返回其他数据,并且根据实现的逻辑可能会产生很大的差异。

因此我想将计时器同步到同一秒。
为了达到目标,我实现了两个计时器: 一秒计时器,启动“真正的”10 秒计时器(目标:整分钟)。 十秒计时器,真正发挥作用。

所以...首先启动第二个计时器并等待 50 秒,然后启动十秒计时器。
意味着 10 秒计时器必须在 hh:mm:00(整分钟)开始,然后每 10 秒触发一次。
在 Windows 客户端中,这就像一个魅力:计时器在整分钟启动,并在任意 10 秒(例如 10:02:00、10:02:10、10:02:10 等)触发整个交易日。

在Windows服务中,这个(与客户端中的代码相同)确实 - 无论出于何种原因 - 不起作用(我不明白): 从一秒计时器开始的十秒计时器工作正常(当我在 Windows 事件日志中写入条目时,我可以看到这一点): 十秒计时器在 50 秒时正确启动(例如 10:01:50)。
但是,它会第一次触发,例如在 10:02:03、10:02:13 等 ,然后“宽松”进一步的时间并更改,例如到 11:02:04 等等。
所以..看起来Windows客户端中的计时器与Windows服务中的计时器的工作方式不同,我不明白(这篇文章的原因)。
备注:

  • windows客户端和windows服务运行在同一台机器上 (使用相同的时钟,通过互联网同步)
  • 我也尝试在服务器上运行 Windows 服务 -> 相同的结果
  • 十秒定时器中的“工作”(通过API查询数据并存储 SQL 服务器上的数据)只需不到一秒

所以...问题是,Windows 服务中的 10 秒计时器在 10 秒后不触发(例如,他第一次在 13 秒后触发),在(正确)启动之后“失败”随着他启动的时间越来越长,我绝对不明白(在 Windows 客户端中从未见过这种行为)。
我也尝试过,在 47 秒(而不是 50 秒)时启动计时器,只是为了看看它是否在整分钟开始,情况并非如此,而且让我感到困惑

代码片段:

定时器定义(在Class()中

 Public TimerDatenAbruf As New System.Timers.Timer() ' 10 second timer that does the work
 Public TimerSekundenSync As New System.Timers.Timer() ' 1 Second timer that starts the 10 second timer

定时器设置(在OnStart()中

 TimerDatenAbruf.Interval = 10 * 1000 ' 10 Seconds for the 10 second timer
 TimerSekundenSync.Interval = 1 * 1000 ' 1 Second for the 1 second timer
' Add of the handlers and start of the one second timer
 AddHandler TimerSekundenSync.Elapsed, AddressOf SekundenTimerEvent 
 TimerSekundenSync.Enabled = True
 TimerSekundenSync.Start()
 AddHandler TimerDatenAbruf.Elapsed, AddressOf TimerDatenAbrufTimerEvent

在 SekundenTimerEvent() 中编写一秒计时器的代码

Private Sub SekundenTimerEvent(source As Object, e As ElapsedEventArgs)
' Sync to the minute
Dim dtTimeStampAktuell As DateTime = System.DateTime.Now
Dim iSekunden As Integer = dtTimeStampAktuell.Second
If iSekunden = 50 Then  
  If Not TimerDatenAbruf.Enabled Then ' only start, if it don't already runs
    TimerDatenAbruf.Start() ' start the ten second timer
    TimerSekundenSync.Stop() ' stop the one second timer
   End If
End If
End Sub

正如我在上面所写的,十秒计时器是通过上述代码(在 50 秒处)正确启动的,但不要将第一次触发到下一分钟,然后随着运行时间的延长而进一步释放时间。 我希望有人能在黑暗中带来一些光明。

预先感谢您的任何提示。

windows vb.net service timer
1个回答
0
投票
如果您根据计时器每 10 秒检查一次系统日期时间,您应该会看到偏差。我不明白为什么你需要两个计时器才能获得一个满足你需求的计时器。

事实是,您必须像这样调试代码,就好像它将不间断地运行整个处理程序,但知道它可能会在每条指令结束时被中断。

尝试将以下代码作为独立应用程序,看看是否有帮助。它尝试将 DateTime.Now.Second 保持为 10 的偶数倍。以防您无法判断它是 Windows 窗体应用程序。

Public Class Form1 Private Shared ReadOnly InitInterval As Integer = CInt(TimeSpan.FromMilliseconds(5).TotalMilliseconds) Public WithEvents TenSecTimer As New System.Timers.Timer(InitInterval) Private Sub TenSecTimer_Elapsed(sender As Object, e As Timers.ElapsedEventArgs) Handles TenSecTimer.Elapsed Static stpw As Stopwatch = Stopwatch.StartNew ' Debug.WriteLine(stpw.ElapsedMilliseconds) Static ProdInterval As Integer = CInt(TimeSpan.FromSeconds(10).TotalMilliseconds) Static drifted As Boolean = False Const driftVal As Long = 250 ' 50 for testing, larger for production i.e. 250. not more than 500 Dim dtNOW As DateTime = DateTime.Now If TenSecTimer.Interval = InitInterval Then Dim sec As Integer = 60 - dtNOW.Second If sec <= 1 Then Threading.Thread.Sleep(57 * 1000) '57 seconds End If dtNOW = DateTime.Now sec = 60 - dtNOW.Second If sec <> 1 Then TenSecTimer.Stop() 'when is second 00, hold until it is sec = 950 * sec Threading.Thread.Sleep(sec) sec = 0 Do While DateTime.Now.Second <> 0 sec += 1 Loop End If Debug.WriteLine(sec.ToString("n0")) TenSecTimer.Interval = ProdInterval TenSecTimer.Start() ElseIf drifted Then 'reset interval drifted = False TenSecTimer.Interval = ProdInterval ElseIf dtNOW.Millisecond >= driftVal Then 'decrease interval drifted = True TenSecTimer.Interval = ProdInterval - driftVal ElseIf dtNOW.Second Mod 10 <> 0 Then ' hmmmmmm Debug.Write("* ") End If stpw.Restart() ' do real work on a different task!!! Dim t As Task t = Task.Run(Sub() ' query the data over the API and store the data on the SQL server Debug.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff")) End Sub) End Sub Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load TenSecTimer.Start() Debug.WriteLine("") Debug.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff")) End Sub End Class
    
© www.soinside.com 2019 - 2024. All rights reserved.