【问题标题】:Best way to do a task looping in Windows Service在 Windows 服务中执行任务循环的最佳方法
【发布时间】:2015-01-27 04:15:33
【问题描述】:

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

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. 方法可以抛出异常,我想写在EventLog上

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

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

【问题讨论】:

  • 你为什么要使用Task呢?为什么不直接使用Timer
  • 使用计时器,我必须在每次触发事件时控制开始和停止。我想过使用这个解决方案,但似乎不是最优雅的。
  • 简单地使用循环怎么样?
  • 因为它会同步运行。我需要异步/同步的混合。

标签: c# multithreading windows-services task-parallel-library async-await


【解决方案1】:

您应该有一个接受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 服务下启动任务?
  • @GuFigueiredo 在服务加载时启动它,在它关闭时等待它。
  • WinService 上的 OnStop 方法不是异步的(不能使用 await)。如果我只是取消令牌它会起作用还是我可以通过其他方式做到这一点? (当我使用 await _task 时,我收到:'await 运算符只能用于标有 async 修饰符的方法')
  • 如果我们在 OnStart 方法中放入 await DoWorkAsync(_cts.Token) 而不是 Task.Run(() => DoWorkAsync(_cts.Token)); 会发生什么?
  • @juzamn OnStart 返回 void 所以它是一个 async void 方法,你应该只用于事件处理程序。
【解决方案2】:

我在 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

    }

【讨论】:

    猜你喜欢
    • 2010-11-11
    • 2016-02-05
    • 1970-01-01
    • 2017-01-27
    • 2013-07-13
    • 1970-01-01
    • 2017-04-24
    • 2018-04-02
    • 1970-01-01
    相关资源
    最近更新 更多