【问题标题】:.NET 4.0 System.Timers.Timer seems to not execute the elapsed event after some time.NET 4.0 System.Timers.Timer 似乎在一段时间后没有执行经过的事件
【发布时间】:2014-11-04 09:46:09
【问题描述】:

我有一个需要 24x7 全天候运行的课程。它使用 Timers.Timer 在计算的时间间隔内执行经过的事件。一段时间后,3-5 天后,计时器停止执行经过的事件。并非所有用户都发生这种情况,大约有 4 或 5 人报告了这种情况。

public class MyClass : IMyClass
{
    public static MyClass thisMyClass;

    private Timer myTimer;
    PERIOD = 60000;
    private SomeHighResolutionClass _lapseTimer;

    private double _timeToNextExec
    {
        get
        {
            double lapseTime = _lapseTimer.LapseTime();
            double next = PERIOD - lapseTime;

            if (next > 0)
            {
                return next;
            }
            else
            {
                return 1;
            }
        }
    }

    private MyClass()
    {
        myTimer = new Timer(PERIOD);
        myTimer.Elapsed += new ElapsedEventHandler(myTimer_Elapsed);
        // Only raise the event the first time Interval elapses.
        myTimer.AutoReset = false;
        myTimer.Enabled = true;
    }

    public void myTimer_Elapsed(Object source, ElapsedEventArgs e)
    {
        LogHere("Elapse started"); // Entry log
        try
        {
            try
            {
                _lapseTimer.Start();
                DoStuffHere();
            }
            catch (Exception ex)
            {
                LogException(ex);
            }
        }
        finally
        {
            myTimer.Interval = _timeToNextExec;
            LogHere("restarting timer interval " + myTimer.Interval); // Finally Log
            myTimer.Start();
        }
    }

    private void DoStuffHere()
    {
        //some processing that could take 1-2 or more seconds to process
    }

    public static void Initialize()
    {
        thisMyClass = new MyClass();    
    }

    public static IMyClass Interface
    {
        get { return thisMyClass as IMyClass; }
    }
}

在另一个类上,这是静态初始化的。

MyClass.Initialize()

定时器间隔是可变的。它基于DoStuffHere(); 的经过时间。如果它花费的时间比 PERIOD 长,那么只要 DoStuffHere 完成,它仍然会被执行。

根据我们收到的数据,elapsed 事件已完成,Entry log 和 finally log 都已成功记录。 我们还确定计时器已根据我们收到的内存转储和来自内存转储的信息重新启动/启用,计时器已启用并且应该触发即使但没有触发。当我们已经观察到DoStuffHere() 没有被执行时,就获得了内存转储。登录LogExceptions();也不例外。

内存转储中的计时器信息

70dcf7dc  40001e0        4        System.Object  0 instance 00000000 __identity
703d7618  40002c3        8 ...ponentModel.ISite  0 instance 00000000 site
703d6b74  40002c4        c ....EventHandlerList  0 instance 00000000 events
70dcf7dc  40002c2       cc        System.Object  0   static 00000000 EventDisposed
70dca574  4002dbd       10        System.Double  1 instance 55949.118673 interval
70dd6820  4002dbe       2c       System.Boolean  1 instance        1 enabled
70dd6820  4002dbf       2d       System.Boolean  1 instance        0 initializing
70dd6820  4002dc0       2e       System.Boolean  1 instance        0 delayedEnable
703ceb80  4002dc1       18 ...apsedEventHandler  0 instance 0246fc8c onIntervalElapsed
70dd6820  4002dc2       2f       System.Boolean  1 instance        0 autoReset
703d5fcc  4002dc3       1c ...SynchronizeInvoke  0 instance 00000000 synchronizingObject
70dd6820  4002dc4       30       System.Boolean  1 instance        0 disposed
70dcacfc  4002dc5       20 ...m.Threading.Timer  0 instance 0246fcb8 timer
70dcb07c  4002dc6       24 ...ing.TimerCallback  0 instance 0246fc6c callback
70dcf7dc  4002dc7       28        System.Object  0 instance 0246fcac cookie

我真的很困惑和无能为力。请帮助各位。

--更新 - 有关线程的附加信息 - 在计时器停止后线程数似乎增加了,因此这可能不是导致我们问题的原因。

0:000> !threads -live
ThreadCount:      809
UnstartedThread:  790
BackgroundThread: 14
PendingThread:    790
DeadThread:       1
Hosted Runtime:   no
                                   PreEmptive   GC Alloc                Lock
       ID  OSID ThreadOBJ    State GC           Context       Domain   Count APT Exception
   0    1   d4c 012be0a0   2006020 Enabled  00000000:00000000 012b7310     0 STA
   2    2   d54 012c9dd0      b220 Enabled  00000000:00000000 012b7310     0 MTA (Finalizer)
   3    3   d60 0131a0c0   100a220 Enabled  00000000:00000000 012b7310     0 MTA (Threadpool Worker)
   4    4   d64 01331ef0      b220 Enabled  00000000:00000000 012b7310     0 MTA
   6    6   d70 01337348   1000220 Enabled  00000000:00000000 012b7310     0 Ukn (Threadpool Worker)
   7    7   d8c 0133f100   2000220 Enabled  00000000:00000000 012b7310     0 Ukn
   9    a   f90 06c2c948   2000220 Enabled  00000000:00000000 012b7310     0 Ukn
  10    b   9a8 06c33f48   200b020 Enabled  00000000:00000000 012b7310     0 MTA
  11    c   584 06c34450   200b020 Enabled  00000000:00000000 012b7310     0 MTA
  12    d   5f4 06c35758   200b020 Enabled  00000000:00000000 012b7310     0 MTA
  13   12   970 06ca6d08   2000220 Enabled  00000000:00000000 012b7310     0 Ukn
  14    e  1254 06ca8b38   2000220 Enabled  00000000:00000000 012b7310     0 Ukn
  15   13  12f0 06ca9548   2000220 Enabled  00000000:00000000 012b7310     0 Ukn
  16    5  10ec 06ca7210   2000220 Enabled  00000000:00000000 012b7310     0 Ukn
  17    f  1d90 06cac290   a009220 Enabled  259e45ac:259e4fe8 012b7310     0 MTA (Threadpool Completion Port)
  19   19  1124 06ca7718   8009220 Enabled  258f90c4:258fafe8 012b7310     0 MTA (Threadpool Completion Port)
  20    8  1394 06ca9040   2000220 Enabled  00000000:00000000 012b7310     0 Ukn
  22  322  21d4 13698f50   2000220 Enabled  259e6394:259e6fe8 012b7310     0 Ukn

干杯!蒂亚!

【问题讨论】:

  • 在无法创建实例的情况下如何使用这个类?
  • @HamletHakobyan,对此感到抱歉,我更新了上面的代码以显示如何使用 MyClass.Initialize() 静态初始化它并访问静态实例,有一个 static IMyClass Interface 对象返回 @987654330 @
  • 您确定(从转储中)间隔设置为正确的值吗?几天后您的线程池是否充分利用?好像有 250 个线程在等待?然后,计时器滴答声无法通过。
  • @usr,是的,转储显示 Interval55949.118673。关于线程,请参阅上面的更新。总而言之,有809个线程,790个未启动,1个是Dead,等等。这是否意味着什么不好?

标签: c# .net


【解决方案1】:

您的线程有问题。 An explanation of what the specific counters mean.

您的应用程序中不应有大约 800 个线程(!)。此外,他们似乎无法完全启动(出于某种原因)。这可能是资源耗尽(可能是内存)。繁重的分页。

找出为什么会有这么多线程。也许您正在许多线程上运行大量阻塞工作,或者将许多此类工作项发布到线程池。

【讨论】:

  • 现在这真的很有启发性,我确实有内存转储,但我真的不知道从哪里开始分析。当我在上面的评论中询问“这是否意味着什么不好?”时,我感到非常“愚蠢”。 :P。我自嘲一笑。谢谢!
  • 我自己从未执行过转储分析,所以我对此一无所知。我将首先对您启动线程或将工作排队到池中的所有位置进行代码审查。
  • 我认为转储中的调用堆栈不会有很大帮助,因为到目前为止线程无法启动。不过值得一试。
  • 你是对的@usr。好家伙。由于线程过多,当 myTimer 被阻止(如您所述)时,内存会增加。但我想知道为什么线程数会飙升到这个数字。我通常有 20-30 个(或者忙时可能有 50 个)。让我回复你。谢谢
  • 嗨@usr,我们收到了用户关于最近计时器故障的新性能日志。该问题似乎不是由线程数引起的。当 Timer 无法运行时,进程中的当前线程数最少(22 个线程),并且在 Timer 无法运行一段时间后,未启动的线程数最终会上升。还有什么想法吗?
【解决方案2】:

如果你的系统实时性不是很严格,你试试

if (next > 10)
        {
            return next;
        }
        else
        {
            return 10;
        }

【讨论】:

  • 谢谢@Lichdr,这可能是一个。但是,根据内存转储,下一个计时器应该在 55949.118673 毫秒后触发 (instance 55949.118673 interval),这意味着 DoStuffHere() 大约在 4 秒后完成。不过还是谢谢。
猜你喜欢
  • 1970-01-01
  • 2019-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-14
  • 1970-01-01
  • 2011-09-08
相关资源
最近更新 更多