System.Threading.Timer 自行停止?

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

有一个类维护一个字典和一个计时器对象。字典通过

System.Threading.Timer
obj 设置的回调定期刷新。这种方法已经工作了很多年,直到最近计时器才自行停止。回调周围有最少的日志记录,并且没有 try catch(我只是将其添加到我的实际代码中),因此我没有关于何时可能再次发生这种情况的信息(未知的根本原因)。

我目前怀疑两件不同的事情。首先是内存问题。此刷新功能作为一个更大的应用程序的一小部分运行,该应用程序是极其内存密集型的(如果您打开任务管理器并检查它,则认为 94-95% 的内存专用于此)。当内存问题变得严重时(97-99%),我想知道机器是否无法获取

ThreadPool
线程并将其提供给回调函数。它运行的机器并不总是相同的,而且离我不远,仅供参考。无论如何,有没有人见过回调找不到线程并且计时器只是随机停止的场景?我认为回调会安排一个线程或其他东西。我正在考虑一种边缘情况,因为我已经对同一服务的小内存实例进行了几天的测试,并且它们可以完美运行很长很长一段时间。然而,承载大量流量的主要网站似乎面临着这场斗争。

或者,如果回调相互堆叠会发生什么。一个例子是,如果

RefreshTheDictionaryMagicMethod()
花费的时间超过一分钟(指定的刷新时间),如果假设这是第一次更新(基本上第一次完全填充字典),那么计时器在第一次回调之前再次到期
ThreadPool 
线程完成。计时器又响了,然后呢?是否有另一个线程被该进程占用,该进程是否已排队?我问这个是因为刷新时间过去是 5 分钟间隔,最近改为 1 分钟。然而,在此之前它以 1 分钟的间隔运行了一个多月,现在它停止得更频繁,因此我认为内存问题的可能性更大。

最后,我现在能做什么?我首先将

Callback
包装在
try{} catch{}
块中,但是内部计时器错误或某些更深层次函数中的错误是否会影响回调?有没有办法看到这些错误?如果捕获到异常,我可以以某种方式重新创建计时器并重新初始化回调吗?

这是代码的示例

public class RefreshDict : IDisposable
{

public Timer myTimer;

public Dictionary<string, string> myDict;

public RefreshDict()
{
}

public void SetCallback()
{
    myTimer = new Timer(Callback, null, TimeSpan.FromMinutes(1),
        Timeout.InfiniteTimeSpan);
}

public void Dispose()
{
    if (myTimer != null) 
    {
        myTimer.Dispose();
    }
}

public void Callback(object state)
{
    RefreshTheDictionaryMagicMethod();
    myTimer.Change(TimeSpan.FromMinutes(1));
}

public void RefreshTheDictionaryMagicMethod()
{
    // some query to a db and some updating a myDict object with new values
}

我已经在我的项目中添加了trycatch,但我真的不知道我希望看到什么。我正在寻找详细的边缘情况信息,或者只是对 threading.timer 进行真正深入的技术解释,这可能表明为什么它会随机停止在我身上。这个设置已经工作了好几个月了,只是现在出现了计时器会随机停止的问题。

编辑!!!

忘记更新线程,但 JonasH 是正确的。基本上,将信息拉入字典的东西有一个奇怪的行为,导致第一次拉取为空并且没有日志或任何东西。这导致了沉默的摊位。

这个故事的寓意是,不要相信垃圾遗留代码,不要因为对 null/invalidArg/empty 进行大量垃圾检查而感到难过,并且日志记录非常有用。另外,只是因为高级工程师。可能会说这是安全的,请对所有建议持保留态度!修复了上面的错误,并且还使用了try/catch/finally。此后没有任何问题!

c# timer threadpool
2个回答
2
投票

我最好的猜测是,您的

RefreshTheDictionaryMagicMethod
中出现一些异常,这将阻止计时器重新启动。如果内存使用率较高,则可能由于内存耗尽、内存泄漏或内存碎片而发生这种情况。我不会担心回调的并发调用或计时器本身进入某种无效状态。

您提到了捕获异常,但没有提到如何捕获异常。最重要的是实际记录,或者以其他方式提醒您异常实际上是什么,而不仅仅是吞掉它。您还可以考虑将

Change
调用放在 finally 语句中以确保它始终运行:

public void Callback(object state)
{
   try{
      RefreshTheDictionaryMagicMethod();
   }
   catch(Exception e){
   // log exception, or some other error handling
   }
   finally{
      myTimer.Change(TimeSpan.FromMinutes(1));
   }
}

请注意,异常可能表明您的应用程序中存在严重错误,如果您总是重新启动计时器,则可能会使该错误变得更糟。因此,普遍的做法是记录异常,让应用程序崩溃,然后修复导致问题的任何原因。如果内存不足,请减少内存占用或购买更多内存。您还可以考虑将进程作为服务运行以使用内置的重新启动功能。


0
投票

抱歉,我无法解决您的实际问题。在我看来,您不需要调用 Timer 的 Change() 方法来按时执行某些操作。只需要在定时器初始化时设置定时间隔即可;

一些代码可能如下所示:

public void SetCallback()
{
    /* call the Callback function immediately, 
then call the Callback function every minute */
    myTimer = new Timer(Callback, null, 0, 60000);
}

public void Dispose()
{
    if (myTimer != null)
    {
        myTimer.Dispose();
    }
}

public void Callback(object state)
{
    RefreshTheDictionaryMagicMethod();
}
© www.soinside.com 2019 - 2024. All rights reserved.