Windows Service System.Timers.Timer未触发

时间:2011-12-11 19:02:25

标签: .net service windows-services timer

我有一个用C#编写的Windows服务,用于每隔几分钟执行一次任务。我正在使用System.Timers.Timer,但它似乎没有出现过。我已经在SO和其他地方看过很多不同的帖子了,我没看到我的代码有什么问题。

这是我的代码,为清晰起见,删除了与非计时器相关的项目......

namespace NovaNotificationService
{
    public partial class NovaNotificationService : ServiceBase
    {
        private System.Timers.Timer IntervalTimer;
        public NovaNotificationService()
        {
            InitializeComponent();
            IntervalTimer = new System.Timers.Timer(60000);  // Default in case app.config is silent.
            IntervalTimer.Enabled = false;
            IntervalTimer.Elapsed += new ElapsedEventHandler(this.IntervalTimer_Elapsed);
        }

        protected override void OnStart(string[] args)
        {
            // Set up the timer...
            IntervalTimer.Enabled = false;
            IntervalTimer.Interval = Properties.Settings.Default.PollingFreqInSec * 1000;
            // Start the timer and wait for the next work to be released...
            IntervalTimer.Start();
        }

        protected override void OnStop()
        {
            IntervalTimer.Enabled = false;
        }

        private void IntervalTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {   // Do the thing that needs doing every few minutes...
            DoWork();
        }
    }
}

我真的在这个问题上摸不着头脑。任何人都能发现我出错的傻事吗?

修改 根据建议,我在服务OnStart方法中IntervalTimer.Enabled = true;之前添加了IntervalTimer.Start();。这不能解决问题。

我已将文件跟踪日志记录添加到服务中以确认一些内部结构,并且我确信在OnStart()完成时Timer.Enabled值为true。

6 个答案:

答案 0 :(得分:48)

这是我的解决方法......

经过太多时间寻找答案,我发现了各种各样的文章和博客讨论Windows服务中的计时器。我已经看到了很多的意见,它们都分为三个类别,按频率降序排列:

  1. 请勿使用System.Windows.Forms.Timer因为它不起作用。 (这才有意义)

  2. 请勿使用System.Threading.Timer,因为它不起作用,请改用System.Timers.Timer

  3. 请勿使用System.Timers.Timer,因为它不起作用,请改用System.Threading.Timer

  4. 基于此,我尝试了2.这也是微软推荐的方法,因为他们说System.Timers.Timersuited to "Server applications"

    我发现System.Timers.Timer在我的Windows服务应用程序中不起作用。因此,我已切换到System.Threading.Timer。这是一个麻烦,因为它需要一些重构才能使它工作。

    这大约是我现在的工作代码......

    namespace NovaNotificationService
    {
        public partial class NovaNotificationService : ServiceBase
        {
            private System.Threading.Timer IntervalTimer;
            public NovaNotificationService()
            {
                InitializeComponent();
            }
    
            protected override void OnStart(string[] args)
            {
                TimeSpan tsInterval = new TimeSpan(0, 0, Properties.Settings.Default.PollingFreqInSec);
                IntervalTimer = new System.Threading.Timer(
                    new System.Threading.TimerCallback(IntervalTimer_Elapsed)
                    , null, tsInterval, tsInterval);
            }
    
            protected override void OnStop()
            {
                IntervalTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
                IntervalTimer.Dispose();
                IntervalTimer = null;
            }
    
            private void IntervalTimer_Elapsed(object state)
            {   // Do the thing that needs doing every few minutes...
                // (Omitted for simplicity is sentinel logic to prevent re-entering
                //  DoWork() if the previous "tick" has for some reason not completed.)
                DoWork();
            }
        }
    }
    

    我讨厌“医生,医生,当我这样做时疼痛......”解决方案,但这就是我不得不诉诸的。对于有这个问题的下一个人来说还有一个意见......

答案 1 :(得分:10)

您忘记enable timer设置:

IntervalTimer.Enabled = true;

或致电Start method

IntervalTimer.Start();
protected override void OnStart(string[] args)
{
    // Set up the timer...
    IntervalTimer.Interval = Properties.Settings.Default.PollingFreqInSec * 1000;
    // Start the timer and wait for the next work to be released...
    IntervalTimer.Start();
}

答案 2 :(得分:5)

显然,System.Timers.Timer会隐藏任何异常,悄悄地吞下它们,然后窒息。 当然,您可以在您已添加为计时器的处理程序的方法中处理这些,但是如果在入口处立即抛出异常(在执行第一行代码之前,如果您的方法声明了变量,则会发生这种情况)例如,如果你在一个强名称的DLL中使用了一个你有错误版本的对象,那么你永远不会看到那个异常。

你要和我们一起撕裂你的头发。

或者你可以这样做:

  • 创建一个包装器方法(在try-catch循环中)调用您想要执行的方法。如果这个方法在你身上濒临死亡,那么被包装的方法可以在不杀死计时器的情况下进行异常处理,因为如果你不停止计时器,它就不会发现出现问题。

(我最终停止了计时器,因为如果它失败了,再次尝试对这个特定的应用程序没有任何意义......)

希望这可以帮助那些从Google登陆的人(就像我一样)。

答案 3 :(得分:4)

我还必须切换到System.Threading.Timer。为了使重新分解更容易,其他人更容易,我创建了一个单独的类,包含System.Threading.Timer的实例,并且具有与System.Timers.Timer几乎相同的方法,因此调用代码只需要很少的更改:

/// <summary>
/// Custom Timer class, that is actually a wrapper over System.Threading.Timer
/// </summary>
/// <seealso cref="System.IDisposable" />
internal class Timer : IDisposable
{
    System.Threading.Timer _timer;

    public Timer()
    {

    }
    public Timer(int interval) : this()
    {
        this.Interval = interval;
    }

    public bool AutoReset { get; set; }
    public bool Enabled { get; set; }
    public int Interval { get; set; }
    public Action<object> OnTimer { get; internal set; }

    public void Dispose()
    {
        if (_timer != null)
        {
            _timer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
            _timer.Dispose();
            _timer = null;
        }
    }

    public void Start()
    {
        _timer = new System.Threading.Timer(
            new System.Threading.TimerCallback(OnTimer), null, 0, Interval);
    }
    public void Stop()
    {
        if (_timer != null)
        {
            _timer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
        }
    }
}

我希望这会有所帮助!

答案 4 :(得分:2)

要添加到“user1820848”所写的内容,因为这也是我的问题,如果你的System.timers.timer经过的事件似乎没有被触发,那么将所有放在事件处理程序中在try / catch块中,找出那里的任何问题。我尝试了所有推荐的方法来处理这个问题(或者我认为),包括从system.timers.timer切换到system.threading.timer,这也没有用。

我认为这个问题更加复杂,因为我们很多人正在将我们的应用程序从我们的工作站移动到我们没有任何调试支持的服务器上,我们可以将其连接到正在运行的服务并验证它是否正常工作。因此,您会遇到事件日志消息或tracelistener消息,并且事件不会触发是完全奇怪的。

我有一种情况,我在这台服务器上有三个运行服务,运行基本相同的计时器代码。我甚至一行一行地使用另一个运行服务的代码,以确保我正在处理system.timers.timer。但是其他服务工作正常,而且这个似乎根本没有解雇这个事件。

事实证明,问题在于,在我最初的昏暗语句中,我正在尝试连接到Oracle的类。该调用失败了,但实际上失败了,因为我的工作站和服务器上的Oracle客户端版本略有不同。它发生在CLR解析引用时,所以它没有被我的底层类try / catch块捕获。如果我正在调试,调试器会标记错误。在服务器上运行,CLR无法告诉我有关该问题的信息。所以我的服务只是坐在那里没有发现错误。

所有内容放入try / catch中,立即指出了问题。在该子例程中的任何声明之前尝试。如果你在早期的声明中失败了,那就是你将如何发现它。

[对不起单独的答案,但你必须提供答案,以获得足够的声誉甚至评论别人的答案?!?]

[编辑:要尝试的另一件事是将您的代码从计时器事件中取出,将其放入另一个子/函数,从您的启动代码调用它,并将函数调用放入您的计时器事件中。几周后,回到我的工作站,尝试运行相同的代码,我有那种下沉的感觉,我的计时器事件没有被调用,我以前来过这里。确实!但是把所有东西放在try / catch中也不起作用!?!将它移动到函数调用和Bam,我的异常 - 再次是Oracle。但是在try / catch中的每一行都没有出现,直到我将代码移出计时器事件并再次尝试。]

答案 5 :(得分:-2)

 private void IntervalTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {   // Do the thing that needs doing every few minutes...
        DoWork();

        //Add following 2 lines. It will work.
        **IntervalTimer.Interval= 100; //any value
        IntervalTimer.Start();**
    }