.net Windows 服务 System.Timers.Timer 未触发

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8466597/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 16:06:00  来源:igfitidea点击:

Windows Service System.Timers.Timer not firing

.netservicewindows-servicestimer

提问by Joel Brown

I have a Windows service written in C# which is meant to perform a task every few minutes. I'm using a System.Timers.Timerfor this but it doesn't ever appear to fire. I've looked at manydifferent posts here on SO and elsewhere and I'm not seeing what is wrong with my code.

我有一个用 C# 编写的 Windows 服务,它每隔几分钟执行一次任务。我System.Timers.Timer为此使用了 a但它似乎从未触发过。我在 SO 和其他地方查看了许多不同的帖子,但我没有看到我的代码有什么问题。

Here is my code, with non-timer related items removed for clarity...

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

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();
        }
    }
}

I'm really scratching my head over this one. Can anybody spot what silly thing I'm getting wrong?

我真的为这个而挠头。任何人都可以发现我做错了什么愚蠢的事情吗?

EDIT:By suggestion, I added IntervalTimer.Enabled = true;before IntervalTimer.Start();in the service OnStart method. This doesn't resolve the issue.

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

I've added file trace logging into the service to confirm some of the internals and I know for sure that the Timer.Enabled value is true by the time OnStart() is finished.

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

回答by Joel Brown

Here is my work-around...

这是我的解决方法...

After way too many hours searching for an answer to this, I discovered a wide variety of articles and blogs discussing timers in Windows services. I've seen a lotof opinions on this and they all fall into three categories and in descending order of frequency:

在为此寻找答案花费了太多时间后,我发现了大量讨论 Windows 服务中的计时器的文章和博客。我已经看到很多关于此的意见,它们都分为三类,按频率降序排列:

  1. Don't use System.Windows.Forms.Timerbecause it won't work. (this only makes sense)

  2. Don't use System.Threading.Timerbecause it doesn't work, use System.Timers.Timerinstead.

  3. Don't use System.Timers.Timerbecause it doesn't work, use System.Threading.Timerinstead.

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

  2. 不要使用,System.Threading.Timer因为它不起作用,System.Timers.Timer而是使用。

  3. 不要使用,System.Timers.Timer因为它不起作用,System.Threading.Timer而是使用。

Based on this, I tried 2. This is also the approach that seems to be recommended by Microsoft since they say that System.Timers.Timeris suited to "Server applications".

在此基础上,我试图2.这也似乎被微软推荐的,因为他们说,该方法System.Timers.Timer适用于“服务器应用程序”

What I've found is that System.Timers.Timerjust doesn't work in my Windows Service application. Therefore I've switched over to System.Threading.Timer. It's a nuisance since it requires some refactoring to make it work.

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

This is approximately what my working code looks like now...

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

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();
        }
    }
}

I hate the "Doctor, doctor, it hurts when I do this..." solution, but that's what I had to resort to. One more opinion on the pile for the next guy with this problem...

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

回答by Micha? Powaga

You forget to enable timerby setting:

您忘记通过设置启用计时器

IntervalTimer.Enabled = true;

or calling Startmethod:

或调用Start方法

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();
}

回答by Peter Huppertz

Apparently, System.Timers.Timer hides any exceptions, swallows them quietly, and then chokes. Of course, you can handle these in your method that you've added as a handler to your timer, but if the exception is thrown immediately on entrance (before the first line of code is executed, which can happen if your method declares a variable that uses an object in a strong-named DLL of which you have the wrong version, for instance), you are never going to see that exception.

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

And you are going to join us all in tearing your hair out.

你将和我们一起撕掉你的头发。

Or you could do this:

或者你可以这样做:

  • create a wrapper method that (in a try-catch loop) calls the method you would like to have executed. If this method is dying on you, the wrapped method can do the exception handling, without killing the timer, because if you do not stop the timer, it will never notice something went wrong.
  • 创建一个包装方法(在 try-catch 循环中)调用您想要执行的方法。如果这个方法快要死了,被包装的方法可以做异常处理,而不会杀死定时器,因为如果你不停止定时器,它永远不会注意到出现问题。

(I did end up stopping the timer, because if it fails, trying again makes no sense for this particular application...)

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

Hope this helps those who landed here from Google (as did I).

希望这能帮助那些从谷歌登陆这里的人(和我一样)。

回答by URB

I also had to switch to System.Threading.Timer. To make re-factoring easier and others live easy, I created a separate class, containing an instance of System.Threading.Timer and has almost the same methods as System.Timers.Timer, so that calling code requires minimal changes:

我还必须切换到 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);
        }
    }
}

I hope this will help!

我希望这个能帮上忙!

回答by user3224303

To add to what "user1820848" wrote, because that was my problem as well, if your System.timers.timer elapsed event doesn't seem to be firing, put everythingin the event handler in a try/catch block, and look for any problem there. I tried all of the recommended methods to deal with this problem (or thought I had), including switching from system.timers.timer to system.threading.timer, and that didn't work either.

添加到“user1820848”所写的内容中,因为这也是我的问题,如果您的 System.timers.timer elapsed 事件似乎没有触发,请将事件处理程序中的所有内容放在 try/catch 块中,然后查找有任何问题。我尝试了所有推荐的方法来处理这个问题(或者我认为我有),包括从 system.timers.timer 切换到 system.threading.timer,但也没有奏效。

I think the problem is compounded because many of us are moving our applications from our workstation, where we can attach to the running service and verify that it works, to a server where we don't have any debugging support. So you're stuck with event log messages or tracelistener messages, and it's completely odd that the event doesn't fire.

我认为问题是复杂的,因为我们中的许多人正在将我们的应用程序从我们的工作站转移到我们没有任何调试支持的服务器上,在那里我们可以附加到正在运行的服务并验证它是否有效。所以你被事件日志消息或跟踪侦听器消息困住了,而且事件没有触发是完全奇怪的。

I had a situation where I have three running services on this server, running essentially the same timer code. I even went line by line with another running service's code to make sure I was doing the system.timers.timer handling the same. But the other service works fine, and this one didn't seem to be firing the event at all.

我遇到过这样的情况,我在这台服务器上有三个正在运行的服务,它们基本上运行相同的计时器代码。我什至逐行查看另一个正在运行的服务的代码,以确保我正在执行 system.timers.timer 处理相同的操作。但是其他服务工作正常,而这个服务似乎根本没有触发事件。

The problem, as it turned out, was that in my initial dim statements I was firing up a class that was trying to connect to Oracle. That call was failing, but it was actually failing because the Oracle client version on my workstation and server was slightlydifferent. It happened when the CLR was resolving the references, so it wasn't caught in my underlying class try/catch blocks. If I were debugging, the debugger would have flagged the error. Running on the server, the CLR had no way to tell me about the problem. So my service just sat there on an untrapped error.

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

Putting everythingin a try/catch immediately pointed out the problem. Put your try before any declarations in that subroutine. If you're failing on a very early statement, that's how you'll catch it.

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

[Sorry for the separate answer, but you have to provide answers to get enough reputation to even comment on someone else's answer?!?]

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

[Edit: another thing to try is take your code out of the timer event, put it into another sub/function, call that from your startup code, and also put the function call in your timer event. Weeks later, back at my workstation, trying to run the same code, and I have that sinking feeling that my timer event isn't getting called, and I've been here before. Indeed! But putting everything in a try/catch isn't working either!?! Moved it to a function call and Bam, there's my exception - Oracle again. But it wasn't coming up even with every single line inside a try/catch, until I moved the code out of the timer event and tried again.]

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

回答by Kausik S

 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();**
    }