2012-03-24 3 views
2

Работает на службе Windows, которая должна обрабатывать запрос в каждый предопределенный промежуток времени. Thread.Sleep отлично работает, но проблема в том, что при вызове службы останавливается, замораживание службы, если поток находится в спящем режиме. Я читал об альтернативном подходе, таком как Timer, но проблема с этим заключается в том, что этот определенный интервал начинается с нового потока. Есть ли лучший способ добиться такого же результата и не запускаться.Альтернатива Thread.Sleep

+2

Запустите новую тему и поставьте ее, чтобы спать, а не спать. Кажется, это проблема дизайна. –

+0

Почему бы вам не пойти на таймер, который просыпается через равные промежутки времени? – GETah

+3

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

ответ

2

Вместо этого вы можете использовать WaitHandle.WaitOne. Вы можете дождаться закрытия события для запуска или таймаута, который вы указываете в предопределенный интервал времени.

static AutoResetEvent seviceStopRequested = new AutoResetEvent(false); 
.... 
((AutoResetEvent)stateInfo).WaitOne([timeout], false) 

Затем, когда служба остановки вызывается, вы можете просто вызвать событие

seviceStopRequested .Set(); 
1

Используйте таймер, чтобы добавить команды/задачи, включая задачи для завершения работы в очередь блокировки. Сделайте свою службу поток ждать задач в очереди блокировки и выполнять их, когда они доступны. Поток таймера будет периодически добавлять задачи в очередь.

3

Что вы ищете - это способность реагировать на уведомление о двух разных событиях - (1), когда таймер истекает, и (2) когда служба остановлена. @Anurag Ranhjan находится на правильном пути с WaitHandle, но у вас есть два события, а не один. Чтобы правильно справиться с этим, выполните следующие действия.

Сначала определите два события, которые вас волнуют, используя ManualResetEvent. Вы можете использовать AutoResetEvent, если хотите; Я просто предпочитаю перезагружать события вручную.

using System.Threading; 
ManualResetEvent shutdownEvent = new ManualResetEvent(); 
ManualResetEvent elapsedEvent = new ManualResetEvent(); 

Вам необходимо активировать эти события, когда они происходят. Для shutdownEvent это легко. В обратном вызове OnStop вашей службы Windows просто установите это событие.

protected override void OnStop 
{ 
    shutdownEvent.Set(); 
} 

Для elapsedEvent, вы могли бы сделать это несколько различных способов. Вы можете создать фоновый поток, то есть ThreadPool, который использует Thread.Sleep. Когда поток просыпается, установите elapsedEvent и вернитесь спать. Так как это фоновый поток, он не повредит ваш сервис, когда он отключится. Альтернатива, как вы уже сказали, заключается в использовании таймера. Вот как я это делаю.

using System.Timers; 
Timer timer = new Timer(); 
timer.Interval = 5000; // in milliseconds 
timer.Elapsed += delegate { elapsedEvent.Set(); }; 
timer.AutoReset = false; // again, I prefer manual control 
timer.Start(); 

Теперь, когда у вас есть события, которые установлены правильно, поместите их в WaitHandle массиве.

WaitHandle[] handles = new WaitHandle[] 
{ 
    shutdownEvent, 
    elapsedEvent 
}; 

Вместо метода WaitHandle.WaitOne, использовать метод WaitHandle.WaitAny внутри цикла в то время, как это.

while (!shutdownEvent.WaitOne()) 
{ 
    switch (WaitHandle.WaitAny(handles)) 
    { 
     case 0: // The shutdownEvent was triggered! 
      break; 
     case 1: // The elapsedEvent was triggered! 
      Process();    // do your processing here 
      elapsedEvent.Reset(); // reset the event manually 
      timer.Start();   // restart the timer manually 
      break; 
     default: 
      throw new Exception("unexpected switch case"); 
    } 
} 

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

+2

Пожалуйста, не рекомендуем 'System.Timers.Timer'. Он ест неискушенные исключения, что заставляет все работать, хотя это может и не быть. Измените пример, который вместо этого использует 'System.Threading.Timer'. – jgauffin

0

Для чего стоит большая часть блокирующих вызовов в .NET BCL будет отвечать на Thread.Interrupt. То есть, они не будут ждать полного количества времени, указанного при вызове, и вместо этого возвращаются немедленно.Однако я бы избегал использовать этот метод и вместо этого использовал один ManualResetEvent для выполнения как ожидания ожидания, так и сигнала останова. Это будет выглядеть так.

public class MyServer : ServiceBase 
{ 
    private ManualResetEvent shutdown = new ManualResetEvent(false); 

    protected override void OnStart(string[] args) 
    { 
    new Thread(
    () => 
     { 
     while (!shutdown.WaitOne(YourInterval)) 
     { 
      // Do work here. 
     } 
     }).Start(); 
    } 

    protected override void OnStop() 
    { 
    shutdown.Set(); 
    } 
} 
2

Я обычно использую следующую закономерность:

public class MyJob 
{ 
    System.Threading.Timer _timer; 
    bool _isStopped; 

    public void MyJob() 
    { 
     _timer = new Timer(OnWork, null, TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(-1)); 
    } 

    private void OnWork(object state) 
    { 
     //[.. do the actual work here ..] 

     if (!_isStopped) 
      _timer.Change(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(-1)); 
    } 

    public void Stop() 
    { 
     _isStopped = true; 
     _timer.Change(TimeSpan.FromSeconds(-1), TimeSpan.FromSeconds(-1)); 
    } 

    public void Start() 
    { 
     _isStopped = false; 
     _timer.Change(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(-1)); 
    } 
} 

Ключевые моменты:

  • Только используя исходный интервал дает вам полный контроль, когда таймер запускается снова (т.е. работы время не подсчитывается в интервале таймера)
  • Изменение таймера на -1 секунд приостанавливает его до изменения

Поэтому он должен работать со всеми вашими требованиями.

Смежные вопросы