Синхронизация сервиса с таймером - PullRequest
2 голосов
/ 25 сентября 2010

Я пытаюсь написать сервис на c #, который должен быть запущен через определенный интервал (тайм-аут) с указанной даты. Если дата наступит в будущем, служба должна подождать, пока не будет достигнута дата.

Пример:

  • Если я установлю тайм-аут на 1 час с 21:00:00, я хочу, чтобы программа запускалась каждый час

  • Если я установлю тайм-аут равным 1 часу с 3999.01.01 21:00:00, я хочу, чтобы программа работала до даты и затем запускалась каждый час

Я добился этого с помощью следующего кода, но у него есть некоторые проблемы!

  1. При установке службы (с помощью installutil) служба помечается как начиная с из-за Thread.Sleep (). Эта служба, кажется, зависает и "устанавливает" до запуска.

  2. Код внутри ServiceTimer_Tick () может занять больше времени, чем ожидалось. Как я могу предотвратить увеличение стека таймера, если это произойдет?

Альтернативы, о которых я думал:

  • включают использование 'timeout.Interval' в первый раз, а затем сброс его последующие вызовы, но это не так.

  • Я также рассмотрел возможность отказаться от всей идеи сервиса, скомпилировать ее как исполняемый файл и настроить запланированные задачи.

Сокращенный пример:

public Service()
{
    _timeout = new TimeSpan(0,1,0,0);
    _timer = new System.Timers.Timer();
    _timer.Interval = _timeout.TotalMilliseconds;
    _timer.Elapsed += new ElapsedEventHandler(ServiceTimer_Tick);            
}

private void ServiceTimer_Tick(object sender, System.Timers.ElapsedEventArgs e)
{            
    lock (_obj)  
    { 
        // Stuff that could take a lot of time
    }
}

public static void Main()
{
    Run(new Service());
}

protected override void OnStart(string[] args)
{
    long current = DateTime.Now.Ticks;
    long start = new DateTime(2010,9,15,21,0,0).Ticks;
    long timeout = _timeout.Ticks;
    long sleep;

    if (current > start)
        sleep = timeout - ((current % timeout)) + (start % timeout);
    else
        sleep = start - current;

    Thread.Sleep(new TimeSpan(sleep));

    _timer.AutoReset = true;
    _timer.Enabled = true;
    _timer.Start();
}

Ответы [ 3 ]

2 голосов
/ 25 сентября 2010

Это проще с System.Threading.Timer.Вы можете указать, сколько времени ждать до первого тика, а затем как часто после этого тика.

Итак, если вы хотите подождать 2 дня до запуска, а затем делать что-то один раз в час, вы 'd напишите:

Timer MyTimer = new Timer(TimerCallback, null, TimeSpan.FromHours(48), TimeSpan.FromHours(1));

Тем не менее, если это то, что нужно запускать только раз в час, то звучит так, будто вам действительно нужен исполняемый файл, который вы затем запланируете с помощью планировщика задач Windows.

2 голосов
/ 25 сентября 2010

Вы можете использовать System.Threading.Timer. Он поддерживает как dueTime, так и period, что вам нужно.

1 голос
/ 25 сентября 2010

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

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

Процедура Run этого потока может выглядеть следующим образом:

    public void run()
    {
        while (processing)
        {
            //initiate action on every full hour
            if (DateTime.Now.Second == 0 && DateTime.Now.Minute == 0)
            {
                //Do something here
                DoSomething();
                //Make sure we sleep long enough that datetime.now.second > 0
                Thread.Sleep(1000);
            }
            Thread.Sleep(100);
        }
    }
...