在 Windows 服务中执行任务循环的最佳方法

本文关键字:最佳 方法 循环 执行任务 Windows 服务 | 更新日期: 2023-09-27 18:32:25

我有一个方法可以向我们的客户发送一些短信,如下所示:

public void ProccessSmsQueue()
{
   SmsDbContext context = new SmsDbContext();
   ISmsProvider provider = new ZenviaProvider();
   SmsManager manager = new SmsManager(context, provider);
   try
   {
      manager.ProcessQueue();
   }
   catch (Exception ex)
   {
      EventLog.WriteEntry(ex.Message, EventLogEntryType.Error);
   }
   finally
   {
      context.Dispose();
   }
}
protected override void OnStart(string[] args)
{
   Task.Factory.StartNew(DoWork).ContinueWith( ??? )
}

所以,我有一些问题:

  1. 我不知道方法运行需要多长时间;

  2. 该方法可能会抛出异常,我想在事件日志上写入

  3. 我想每 10 分钟循环运行此方法,但仅在上次执行完成后。

我怎样才能做到这一点?我想过使用ContinueWith(),但我仍然对如何构建整个逻辑有疑问。

在 Windows 服务中执行任务循环的最佳方法

你应该有一个异步方法,它接受一个CancellationToken,以便它知道何时停止,在try-catch块中调用ProccessSmsQueue,并使用Task.Delay异步等待,直到下次需要运行时:

public async Task DoWorkAsync(CancellationToken token)
{
    while (true)
    {
        try
        {
            ProccessSmsQueue();
        }
        catch (Exception e)
        {
            // Handle exception
        }
        await Task.Delay(TimeSpan.FromMinutes(10), token);
    }
}

您可以在应用程序启动时调用此方法,并在存在之前Task.Wait返回的任务,以便您知道它已完成并且没有异常:

private Task _proccessSmsQueueTask;
private CancellationTokenSource _cancellationTokenSource;
protected override void OnStart(string[] args)
{
    _cancellationTokenSource = new CancellationTokenSource();
    _proccessSmsQueueTask = Task.Run(() => DoWorkAsync(_cancellationTokenSource.Token));
}
protected override void OnStop()
{
    _cancellationTokenSource.Cancel();
    try
    {
        _proccessSmsQueueTask.Wait();
    }
    catch (Exception e)
    {
        // handle exeption
    }
}

我在Windows服务中使用的示例Worker Class。它支持使用锁以"干净"的方式停止。您只需要在 DoWork 中添加代码,在 StartTimerAndWork 方法中设置计时器(以毫秒为单位),然后在服务中使用此类。

public class TempWorker
    {
        private System.Timers.Timer _timer = new System.Timers.Timer();
        private Thread _thread = null;
        private object _workerStopRequestedLock = new object();
        private bool _workerStopRequested = false;
        private object _loopInProgressLock = new object();
        private bool _loopInProgress = false;
        bool LoopInProgress
        {
            get
            {
                bool rez = true;
                lock (_loopInProgressLock)
                    rez = _loopInProgress;
                return rez;
            }
            set
            {
                lock (_loopInProgressLock)
                    _loopInProgress = value;
            }
        }
        #region constructors
        public TempWorker()
        {
        }
        #endregion
        #region public methods
        public void StartWorker()
        {
            lock (_workerStopRequestedLock)
            {
                this._workerStopRequested = false;
            }
            _thread = new Thread(new ThreadStart(StartTimerAndWork));
            _thread.Start();
        }
        public void StopWorker()
        {
            if (this._thread == null)
                return;
            lock (_workerStopRequestedLock)
                this._workerStopRequested = true;
            int iter = 0;
            while (LoopInProgress)
            {
                Thread.Sleep(100);
                iter++;
                if (iter == 60)
                {
                    _thread.Abort();
                }
            }
            //if (!_thread.Join(60000))
            //    _thread.Abort();
        }
        #endregion

        #region private methods
        private void StartTimerAndWork()
        {
            this._timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
            this._timer.Interval = 10000;//milliseconds
            this._timer.Enabled = true;
            this._timer.Start();
        }

        #endregion

        #region event handlers
        private void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (!LoopInProgress)
            {
                lock (_workerStopRequestedLock)
                {
                    if (this._workerStopRequested)
                    {
                        this._timer.Stop();
                        return;
                    }
                }
                DoWork();
            }
        }
        private void DoWork()
        {
            try
            {
                this.LoopInProgress = true;
                //DO WORK HERE
            }
            catch (Exception ex)
            {
                //LOG EXCEPTION HERE
            }
            finally
            {
                this.LoopInProgress = false;
            }
        }
        #endregion
    }