【问题】为什么 System.Timers.Timer 更改间隔时间后的第一次触发时间是设定时间的三倍?

【问题】为什么 System.Timers.Timer 更改间隔时间后的第一次触发时间是设定时间的三倍?

【问题】为什么 System.Timers.Timer 更改间隔时间后的第一次触发时间是设定时间的三倍?

独立观察员 2022年9月4日

【已解决(20220918)】和 Timer 无关,是使用信息窗控件输出方法时的用法不对,没有加 Dispatcher(见最后)。

 

一、问题和现象

在编写 “Wifi 固定器[1]” 程序时,按如下方式使用了定时器

//声明;
private Timer _Timer = new Timer() { Interval = 1, AutoReset = true };

//设置处理方法;
_Timer.Elapsed += new ElapsedEventHandler(TimerHandler);

/// <summary>
/// 定时器任务
/// </summary>
private async void TimerHandler(object source, ElapsedEventArgs e)
{
    if (_Timer.Interval == 1) //如果是第一次执行
    {
        _Timer.Interval = 1000 * Configs.CheckInterval; //设置Interval为想要的间隔时间。
    }
    
    //刷新连接状态;
    _profileRadio = GetProfileRadio(_fixedWifiPack);
    if (_profileRadio.IsConnected)
    {
        Console.WriteLine("该 Wifi 已连接,无需操作");
        return;
    }

    Console.WriteLine($"即将尝试连接【{_fixedWifiPack.Ssid}】...");
    bool result = await NativeWifi.ConnectNetworkAsync(_fixedWifiPack.Interface.Id, _fixedWifiPack.ProfileName,
        _fixedWifiPack.BssType, TimeSpan.FromSeconds(5));
    Console.WriteLine($"连接结果:{(result ? "成功" : "失败")}");
}

//开启
if (_Timer.Enabled)
{
    Console.WriteLine($"目前监控已处于开启状态,无需重复操作");
    return;
}
_Timer.Start();
Console.WriteLine($"【开启监控成功】检测间隔时间为 {Configs.CheckInterval}s");

//关闭
if (!_Timer.Enabled)
{
    Console.WriteLine($"目前监控已处于关闭状态,无需重复操作");
    return;
}
_Timer.Stop();
_Timer.Interval = 1;
Console.WriteLine($"【关闭监控成功】{Environment.NewLine}");

 

现象:

【问题】为什么 System.Timers.Timer 更改间隔时间后的第一次触发时间是设定时间的三倍?插图

 

也就是,Timer 的 Interval 初始以及停止时,都设置为 1,为的是启动的时候能马上触发一次。然后在第一次触发时修改 Interval 为需要的间隔时间,用作后续的触发间隔。然后问题就来了,修改间隔后的那次触发,距离启动时立马触发的那次,间隔时间达到了设定间隔时间的 3 倍,而且每次都是这样。

 

修改时间间隔的地方加上先停止后启动,问题依旧:

【问题】为什么 System.Timers.Timer 更改间隔时间后的第一次触发时间是设定时间的三倍?插图1

 

不使用异步方法,问题依旧:

【问题】为什么 System.Timers.Timer 更改间隔时间后的第一次触发时间是设定时间的三倍?插图2

 

怀疑是和线程池有关系,进而和 CPU 核心数有关,我这个是四核:

【问题】为什么 System.Timers.Timer 更改间隔时间后的第一次触发时间是设定时间的三倍?插图3

 

使用 毫秒定时器[2] 或 多媒体定时器[3] 也还是同样的现象。

 

二、网上的一些解决方法

看到网上也有人遇到类似但不完全相同的问题(《System.Timers.Timer 非常不准确[4]):

【问题】为什么 System.Timers.Timer 更改间隔时间后的第一次触发时间是设定时间的三倍?插图4

 

可以看到,他是使用了并行计算所以出问题了,但是我这里并没有使用并行计算。

 

然后网上一个讨论帖(《System.Timers.Timer 为什么会失效??[5])是这样说的:

【问题】为什么 System.Timers.Timer 更改间隔时间后的第一次触发时间是设定时间的三倍?插图5

 

因为怀疑计时不准,所以有好多人自己封装调用 winmm.dll 中的“多媒体计时器”来形成自定义的定时器,我尝试了两种(上面提到过),问题还是一样,所以可能他们这种不能解决我遇到的问题。

 

三、目前的情况及资料

总之,问题还没解决,所以大家有什么想法或方法,还请不吝赐教。

 

附-参考资料及整理的资料:

1、Wifi 固定器 代码:https://gitee.com/dlgcy/DLGCY_FixedWifi/tree/Blog20220904

2、毫秒定时器(整理版代码):https://gitee.com/dlgcy/dotnetcodes/blob/dlgcy/DotNet.Utilities/%E5%AE%9A%E6%97%B6%E5%99%A8/MillisecondTimer.cs

3、多媒体定时器(原始代码托管):https://gitee.com/dlgcy/dotnetcodes/blob/dlgcy/DotNet.Utilities/%E5%AE%9A%E6%97%B6%E5%99%A8/MultimediaTimer.cs

4、《System.Timers.Timer 非常不准确》:https://qa.1r1g.com/sf/ask/2286140321/

5、《System.Timers.Timer 为什么会失效??》:https://bbs.csdn.net/topics/90487784?list=764574

6、《Timer 计时不准确的解决方案 每次都重新调整,修正误差》(提到多媒体定时器的文章):https://www.cnblogs.com/chucklu/p/4673600.html

7、《Timer 计时不准确的问题及解决方法》(提到毫秒定时器的文章):https://www.cnblogs.com/dehai/p/4347061.html

 

四、大家的讨论

(2022.09.05)

4.1、东吴大都督

【问题】为什么 System.Timers.Timer 更改间隔时间后的第一次触发时间是设定时间的三倍?插图6

 

4.2、谣言似山

【问题】为什么 System.Timers.Timer 更改间隔时间后的第一次触发时间是设定时间的三倍?插图7

20220905:

【问题】为什么 System.Timers.Timer 更改间隔时间后的第一次触发时间是设定时间的三倍?插图8

 

4.3、其它

【问题】为什么 System.Timers.Timer 更改间隔时间后的第一次触发时间是设定时间的三倍?插图9

 

五、阶段性解决(by @谣言似山)

5.1 在方法开始处添加一句输出

https://gitee.com/dlgcy/DLGCY_FixedWifi/pulls/1

昨晚尝试了几种情况,目前怀疑是你 Interval 设置为 1,太短了。
第一次触发还没执行完(async、void)就返回了,又被排了好几个 Event 进了窗口的消息队列。然后这些消息队列里的 event 执行的时候,读取_Timer 的 Interval 又出了什么问题。(仅仅是个猜测)

加 Console.ReadLine 可以解决,怀疑是 Console.Readline 耗时较长,解决了 “读取_Timer 的 Interval” 的问题。(图见链接)

空的 Console.Readline 不能解决问题,可能是因为它不向 stdout 里输出东西,执行的太快。(图见链接)

今早看了你的代码,你是为了 “立刻触发一次” 才搞了 “Interval 设置为 1” 的操作。其实大可不必,可以直接调用一次 TimerHandler 的。

 

这个我也按他说的尝试了一下,确实是可以的,同时测试排除了 Console 重定向的嫌疑:

【问题】为什么 System.Timers.Timer 更改间隔时间后的第一次触发时间是设定时间的三倍?插图10

 

5.2 放弃使用让 Timer“立即触发一次”的方案

_Timer 的 Interval 设置移到_Timer.Start () 之前,_Timer 的 “立即触发一次” 改为在 _Timer.Start () 之后,手动调用一次 TimerHandler,取消停止 Timer 之后,手动把 Interval 改为 1 的操作。

 

改动如下:

【问题】为什么 System.Timers.Timer 更改间隔时间后的第一次触发时间是设定时间的三倍?插图11

 

所以修复后的代码路径为:https://gitee.com/dlgcy/DLGCY_FixedWifi/tree/Blog20220905

感谢感谢!

 

六、断线重连后还是有同样的问题 [已解决]

(2022.09.18)

现象:

【问题】为什么 System.Timers.Timer 更改间隔时间后的第一次触发时间是设定时间的三倍?插图12

 

当前代码:

【问题】为什么 System.Timers.Timer 更改间隔时间后的第一次触发时间是设定时间的三倍?插图13

 

尝试解决:

1、加了 Try-Catch,没有发现异常。

2、打印线程号,看看能不能发现什么问题:

【问题】为什么 System.Timers.Timer 更改间隔时间后的第一次触发时间是设定时间的三倍?插图14

 

发现线程号输出只输出了一次,然后问题还是存在:

【问题】为什么 System.Timers.Timer 更改间隔时间后的第一次触发时间是设定时间的三倍?插图15

 

看来,问题可能出在信息窗控件里,或者是调用信息窗控件输出的这个方法 ShowInfoUc 有问题。

 

3、修改绑定基类中的向信息窗控件输出信息的方法(问题解决)

也就是之前相关代码没有在 Dispatcher 中调用,导致在线程中会有问题,看来有点错怪 Timer 了。

【问题】为什么 System.Timers.Timer 更改间隔时间后的第一次触发时间是设定时间的三倍?插图16

 

已经正常了:

【问题】为什么 System.Timers.Timer 更改间隔时间后的第一次触发时间是设定时间的三倍?插图17

 

去掉线程输出看看,正常:

【问题】为什么 System.Timers.Timer 更改间隔时间后的第一次触发时间是设定时间的三倍?插图18

 

修复后的代码:https://gitee.com/dlgcy/DLGCY_FixedWifi/tree/Blog20220918

 

Leave a Reply