2009-11-13 78 views
3

我编写了一个Timer类,以便在轮询另一个系统的Windows服务中使用它。我这样做是因为我有两个问题,System.Timers.Timer没有解决。投票窗口服务的计时器

  1. Elapsed EventHanler在后台运行,因此如果主线程结束,它的执行将中止。我希望System.Timers.Timer.Stop函数可以阻止主线程,直到Elapsed EventHanler的执行结束。
  2. System.Timers.Timer不涉及事件再入境。我希望间隔在两个Elapsed EventHanler之间,这样如果前一个调用(+间隔)尚未完成,定时器就不会调用Elapsed EventHanler。

在写我发现我需要一些thrading相关的问题,因为我不是太那些我想知道,如果下面的Timer类是线程安全的经验,以DAEL类?

public class Timer 
{ 
    System.Timers.Timer timer = new System.Timers.Timer() { AutoReset = false }; 
    ManualResetEvent busy = new ManualResetEvent(true); 

    public double Interval 
    { 
     get { return timer.Interval; } 
     set { timer.Interval = value; } 
    } 

    public Timer() 
    { 
     timer.Elapsed += new ElapsedEventHandler(TimerElapsed); 
    } 

    void TimerElapsed(object sender, ElapsedEventArgs e) 
    { 
     try 
     { 
      busy.Reset(); 
      OnElapsed(e); 
      timer.Start(); 
     } 
     finally 
     { 
      busy.Set(); 
     } 
    } 

    public event EventHandler Elapsed; 

    protected void OnElapsed(EventArgs e) 
    { 
     if (Elapsed != null) 
     { 
      Elapsed(this, e); 
     } 
    } 

    public virtual void Start() 
    { 
     busy.WaitOne(); 
     timer.Start(); 
    } 

    public virtual void Stop() 
    { 
     busy.WaitOne(); 
     timer.Stop(); 
    } 
} 

回答

5

都可以使用System.Threading.Timer,而不是这个定时器作为根据我的经验,是一个性能更好的定时器(只是个人经验的指教)第一。

其次,在这种情况下,您应该给一个标志,该标志在早期定时器完成任务后设置(该标志 - 一个被所有线程访问的静态字段)。

在这种情况下,请确保在出现任何错误的情况下,标志由您复位,以便其他定时器不会无限等待,因为定时器任务无法为其他定时器设置标志,因为任务内部发生错误(应添加最后一个块的类型以确保处理错误并且标志始终被重置)。

一旦这个标志被重置,然后下一个线程在它上面工作,所以这个检查将确保所有的线程都在一个接一个地启动任务。

我为这样的情况编写的示例代码(方法代码已被删除,这会给你设计细节)。

namespace SMSPicker 
{ 
public partial class SMSPicker : ServiceBase{ 
    SendSMS smsClass; 
    AutoResetEvent autoEvent; 
    TimerCallback timerCallBack; 
    Timer timerThread; 
    public SMSPicker() 
    { 
     InitializeComponent(); 
    } 

    protected override void OnStart(string[] args) 
    { 
     // TODO: Add code here to start your service. 
     smsClass = new SendSMS(); 
     autoEvent = new AutoResetEvent(false); 
     long timePeriod = string.IsNullOrEmpty(ConfigurationSettings.AppSettings["timerDuration"]) ? 10000 : Convert.ToInt64(ConfigurationSettings.AppSettings["timerDuration"]); 
     timerCallBack = new TimerCallback(sendSMS); 
     timerThread = new Timer(timerCallBack, autoEvent, 0, timePeriod); 
    } 


    private void sendSMS(object stateInfo) 
    { 
     AutoResetEvent autoResetEvent = (AutoResetEvent)stateInfo; 
     smsClass.startSendingMessage(); 
     autoResetEvent.Set(); 
    } 

    protected override void OnStop() 
    { 
     // TODO: Add code here to perform any tear-down necessary to stop your service. 
     smsClass.stopSendingMessage(); 
     timerThread.Dispose();    

    } 
} 
} 







namespace SMSPicker 
{ 
class SendSMS 
{ 
    //This variable has been done in order to ensure that other thread does not work till this thread ends 
    bool taskDone = true; 
    public SendSMS() 
    { 

    } 

    //this method will start sending the messages by hitting the database 
    public void startSendingMessage() 
    { 

     if (!taskDone) 
     { 
      writeToLog("A Thread was already working on the same Priority."); 
      return; 
     } 

     try 
     { 
     } 
     catch (Exception ex) 
     { 
      writeToLog(ex.Message); 
     } 
     finally 
     { 
      taskDone = stopSendingMessage(); 

      //this will ensure that till the database update is not fine till then, it will not leave trying to update the DB 
      while (!taskDone)//infinite looop will fire to ensure that the database is updated in every case 
      { 
       taskDone = stopSendingMessage(); 
      } 
     } 

    } 


public bool stopSendingMessage() 
    { 
     bool smsFlagUpdated = true; 
     try 
     { 

     } 
     catch (Exception ex) 
     { 
      writeToLog(ex.Message); 
     } 
     return smsFlagUpdated; 
    } 

} 
} 
0

我有一个类似的问题:更简单的事情可以是使用System.Threading.Timer与period = 0;

在这种情况下,调用一次回调方法。然后,重置定时器(调用它的Change()方法)再次打开;在你重新发生的方法结束时。

这种情况在此说明:http://kristofverbiest.blogspot.com/2008/08/timers-executing-task-at-regular.html

+1

此解决方案不能解决1中描述的问题。 – Martin 2009-11-25 08:38:36

0

这样做将是等待一个事件而不是使用计时器的另一种方式。

public class PollingService Private Thread _workerThread; private AutoResetEvent _finished; private const int _timeout = 60 * 1000;

public void StartPolling() 
{ 
    _workerThread = new Thread(Poll); 
    _finished = new AutoResetEvent(false); 
    _workerThread.Start(); 
} 

private void Poll() 
{ 
    while (!_finished.WaitOne(_timeout)) 
    { 
     //do the task 
    } 
} 

public void StopPolling() 
{ 
    _finished.Set(); 
    _workerThread.Join(); 
} 

}

在服务

public partial class Service1 : ServiceBase 
{ 
    private readonly PollingService _pollingService = new PollingService(); 
    public Service1() 
    { 
     InitializeComponent(); 
    } 

    protected override void OnStart(string[] args) 
    { 
     _pollingService.StartPolling(); 
    } 

    protected override void OnStop() 
    { 
     _pollingService.StopPolling(); 
    } 

} 
0

你可以使用定时器或专用线程/任务睡觉或什么的。就个人而言,我发现专用线程/任务比定时器更容易处理这些事情,因为它更容易控制轮询间隔。此外,您一定要使用TPL提供的协作取消机制。它没有必要抛出异常。它只有在您致电ThrowIfCancellationRequested时才这样做。您可以使用IsCancellationRequested来检查取消标记的状态。

下面是一个非常通用的模板,您可以使用它开始。

public class YourService : ServiceBase 
{ 
    private CancellationTokenSource cts = new CancellationTokenSource(); 
    private Task mainTask = null; 

    protected override void OnStart(string[] args) 
    { 
    mainTask = new Task(Poll, cts.Token, TaskCreationOptions.LongRunning); 
    mainTask.Start(); 
    } 

    protected override void OnStop() 
    { 
    cts.Cancel(); 
    mainTask.Wait(); 
    } 

    private void Poll() 
    { 
    CancellationToken cancellation = cts.Token; 
    TimeSpan interval = TimeSpan.Zero; 
    while (!cancellation.WaitHandle.WaitOne(interval)) 
    { 
     try 
     { 
     // Put your code to poll here. 
     // Occasionally check the cancellation state. 
     if (cancellation.IsCancellationRequested) 
     { 
      break; 
     } 
     interval = WaitAfterSuccessInterval; 
     } 
     catch (Exception caught) 
     { 
     // Log the exception. 
     interval = WaitAfterErrorInterval; 
     } 
    } 
    } 
}