Windows Service System.Timers.Timer не запускается - PullRequest
30 голосов
/ 11 декабря 2011

У меня есть служба Windows, написанная на C #, которая предназначена для выполнения задачи каждые несколько минут.Я использую 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();
        }
    }
}

Я действительно чесаю голову над этим.Кто-нибудь может определить, какая глупая вещь, в которой я ошибаюсь?

РЕДАКТИРОВАТЬ: По предложению я добавил IntervalTimer.Enabled = true; до IntervalTimer.Start(); в методе OnStart службы.Это не решает проблему.

Я добавил регистрацию трассировки файлов в службу, чтобы подтвердить некоторые внутренние компоненты, и я точно знаю, что значение Timer.Enabled равно true, когда OnStart ()завершено.

Ответы [ 6 ]

48 голосов
/ 15 декабря 2011

Вот мой обходной путь ...

После долгих часов поиска ответа на этот вопрос я обнаружил множество статей и блогов, обсуждающих таймеры в службах Windows.,Я видел много мнений по этому вопросу, и все они делятся на три категории и в порядке убывания частоты:

  1. Не используйте System.Windows.Forms.Timer, потому чтоэто не сработает.(это имеет смысл только)

  2. Не используйте System.Threading.Timer, потому что он не работает, вместо этого используйте System.Timers.Timer.

  3. Не используйте System.Timers.Timer, потому что это не работает, вместо этого используйте System.Threading.Timer.

Исходя из этого, я попробовал 2. Это также подход, который, кажется, рекомендуетсяMicrosoft, поскольку они говорят, что System.Timers.Timer подходит для "Серверных приложений" .

Я обнаружил, что System.Timers.Timer просто не работает в моем приложении Windows Service.Поэтому я переключился на 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();
        }
    }
}

Я ненавижу решение "Доктор, доктор, мне больно, когда я делаю это ...", но эток чему я должен был прибегнуть.Еще одно мнение о куче для следующего парня с этой проблемой ...

10 голосов
/ 11 декабря 2011

Вы забыли включить таймер , установив:

IntervalTimer.Enabled = true;

или вызов 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();
}
5 голосов
/ 07 февраля 2013

Очевидно, System.Timers.Timer скрывает любые исключения, тихо глотает их, а затем задыхается. Конечно, вы можете обрабатывать их в своем методе, который вы добавили в качестве обработчика к вашему таймеру, но если исключение выдается сразу при входе (до выполнения первой строки кода, что может произойти, если ваш метод объявит переменную который использует объект в DLL со строгим именем, например, с неверной версией), вы никогда не увидите это исключение.

И вы присоединитесь к нам всем, чтобы вырвать себе волосы.

Или вы можете сделать это:

  • создайте метод-оболочку, который (в цикле try-catch) вызывает метод, который вы хотели бы выполнить. Если этот метод умирает от вас, упакованный метод может выполнять обработку исключений, не убивая таймер, потому что, если вы не остановите таймер, он никогда не заметит, что что-то пошло не так.

(Я остановил таймер, потому что, если он не работает, повторная попытка не имеет смысла для этого конкретного приложения ...)

Надеюсь, это поможет тем, кто попал сюда из Google (как я).

4 голосов
/ 24 июля 2016

Мне также пришлось переключиться на 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);
        }
    }
}

Надеюсь, это поможет!

2 голосов
/ 16 июля 2014

Чтобы добавить к тому, что написал "user1820848", потому что это тоже была моя проблема, если ваше событие System.timers.timer, по-видимому, не запускается, поместите everything в обработчик события вблок try / catch и ищите там любую проблему.Я попробовал все рекомендуемые методы для решения этой проблемы (или думал, что имел), включая переключение с system.timers.timer на system.threading.timer, и это тоже не сработало.

Я думаюпроблема усугубляется тем, что многие из нас переносят наши приложения с нашей рабочей станции, где мы можем подключиться к работающему сервису и убедиться, что он работает, на сервер, где у нас нет поддержки отладки.Таким образом, вы застряли с сообщениями журнала событий или сообщениями tracelistener, и совершенно странно, что событие не срабатывает.

У меня была ситуация, когда у меня на этом сервере три запущенных службы, выполняющих по существу один таймеркод.Я даже построчно шел по коду другого работающего сервиса, чтобы убедиться, что я работаю с system.timers.timer так же.Но другой сервис работает нормально, и этот, похоже, вообще не запускал событие.

Проблема, как оказалось, заключалась в том, что в моих первоначальных dim-заявлениях я запускал класс, которыйпытался подключиться к Oracle.Этот вызов не удался, но на самом деле не удался, потому что версия клиента Oracle на моей рабочей станции и сервере отличалась немного .Это произошло, когда CLR разрешал ссылки, поэтому он не был пойман в моих блоках try / catch базового класса.Если бы я отлаживал, отладчик пометил бы ошибку.Работая на сервере, CLR не мог рассказать мне о проблеме.Так что мой сервис просто сидел там на нераспознанной ошибке.

Помещение all в try / catch сразу указало на проблему.Сделайте попытку перед любыми объявлениями в этой подпрограмме.Если вы ошибаетесь в очень раннем утверждении, вот как вы его поймаете.

[Извините за отдельный ответ, но вы должны предоставить ответы, чтобы получить достаточно репутации, чтобы даже комментировать чужой ответ?!?]

[Редактировать: еще одна вещь, которую нужно попробовать, - это взять свой код из события таймера, поместить его в другую подфункцию / функцию, вызвать его из кода запуска, а также поместить вызов функции в свой таймер.событие.Несколько недель спустя, вернувшись на мою рабочую станцию, пытаясь запустить тот же код, и у меня возникает чувство, что мое событие таймера не вызывается, и я был здесь раньше.В самом деле!Но положить все в try / catch тоже не получается!?!Перенес его в вызов функции и Бэм, вот мое исключение - опять Oracle.Но он не появлялся даже с каждой строкой внутри try / catch, пока я не удалил код из события timer и попытался снова.]

0 голосов
/ 01 марта 2013
 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();**
    }
...