【问题标题】:C# Threading.Timer not always triggering when in a windows Service TaskC# Threading.Timer 在 Windows 服务任务中并不总是触发
【发布时间】:2016-03-22 22:19:52
【问题描述】:

伙计们,我正在基于 .NET 的 Windows 服务中实现一个例程,并在其中使用 Threading.Timer 任务

protected override void OnStart(string[] args)
{
   base.OnStart(args);
   _startupTask = Task.Factory.StartNew(StartupAction, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}

在我的 StartupAction 中,我定义了一个 Threading.Timer,如下所示:

private void StartupAction()
{ 
    System.Threading.Timer infamousTimer;
    infamousTimer = new System.Threading.Timer(new TimerCallback(runMeEveryHour), null, new TimeSpan(0), new TimeSpan(1, 0, 0));
}

人们会认为这很简单,但由于某种原因,它并不总是有效(在某些 PC 上有效,但在其他 PC 上无效)。

有谁知道罪魁祸首或环境依赖性是什么?

谢谢

当定时器超出范围时,不会触发回调。请参阅下面的答案

【问题讨论】:

  • 为什么要使用任务?为什么不直接在OnStart 中启动线程计时器?当然,您的实际用例可能更复杂,而您只是对其进行了简化,但如果将其移到任务之外会发生什么?
  • @Lloyd 是的,我可以做得不那么花哨,但为什么会产生差异!

标签: c# multithreading timer windows-services


【解决方案1】:

您的问题在于您使用了Task 并创建了完全包含在任务操作中的计时器:

private void StartupAction()
{ 
    System.Threading.Timer infamousTimer;
    infamousTimer = new System.Threading.Timer(new TimerCallback(runMeEveryHour), null, new TimeSpan(0), new TimeSpan(1, 0, 0));
}

我认为正在发生的事情是,一旦StartupAction 完成,计时器就会超出范围并被处置/GC'd。

而是将计时器变量移到外面:

private Timer infamousTimer;

private void StartupAction()
{ 
    infamousTimer = new System.Threading.Timer(new TimerCallback(runMeEveryHour), null, new TimeSpan(0), new TimeSpan(1, 0, 0));
}

这里的好处还在于OnStop你可以干净地处理定时器。

当然,为什么还要在Task 中这样做?只是在OnStart 中执行此操作似乎有点矫枉过正。

我还想指出,如果所有服务都运行一个计时器,那么你最好安排一个计划任务,但我在那里做一个假设。

请参阅this blog post 了解原因。

【讨论】:

  • 天哪,这是真的,这是有道理的。就是这样,感谢您提供额外的信息。
【解决方案2】:

当计时器超出范围时,不会触发回调。

【讨论】:

    【解决方案3】:

    我认为您的 StartupAction 方法正在结束(在我看来,它超出范围并收集垃圾是副产品)。

    您需要修改您的 StartupAction 方法以使其保持活动状态。

    尝试添加类似:while(true) { Thread.Sleep(1000); } 以保持线程活跃。

    private void StartupAction()
    { 
        System.Threading.Timer infamousTimer;
        infamousTimer = new System.Threading.Timer(new TimerCallback(runMeEveryHour), null, new TimeSpan(0), new TimeSpan(1, 0, 0));
        while(true) { Thread.Sleep(1000); } //this is the simplest logic to keep the thread alive
    }
    

    【讨论】:

    • 当您需要做的只是移动引用计时器的位置时,您会不必要地束缚线程。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-29
    • 1970-01-01
    • 2013-11-02
    相关资源
    最近更新 更多