Best way to do a task looping in Windows Service

Created: 2015-11-05 06:06 Updated: 2015-11-05 06:06 Source: http://stackoverflow.com/questions/27189750/best-way-to-do-a-task-looping-in-wi… Notebook: All Tech/Frontend Development

Sign up ×
Stack Overflow is a community of 4.7 million programmers, just like you, helping each other. Join them, it only takes a minute:

I have a method that send some SMS to our customers that look like below:

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( ??? )
}

So, I have some issues:

  1. I don´t know how long it takes for the method run;

  2. The method can throw exceptions, that I want to write on EventLog

  3. I want to run this method in loop, every 10 min, but only after last execution finish.

How I can achieve this? I thought about using ContinueWith(), but I still have questions on how to build the entire logic.

asked Nov 28 '14 at 13:28
GuFigueiredo
198211
1  
Why do you want to do it using a Task at all? Why not just use a Timer? – Ben Robinson Nov 28 '14 at 13:33
    
Using a timer, I would have to control the start and stop each time the event was fired. I thought of using this solution, but did not seem the most elegant. – GuFigueiredo Nov 28 '14 at 13:36
    
What about simply using a loop? – usr Nov 29 '14 at 21:25
    
Because it will run synchronously. I need a mix of async/sync. – GuFigueiredo Dec 1 '14 at 12:07

2 Answers

up vote 6 down vote accepted

You should have an async method that accepts a CancellationToken so it knows when to stop, calls ProccessSmsQueue in a try-catch block and uses Task.Delay to asynchronously wait until the next time it needs to run:

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

You can call this method when your application starts and Task.Wait the returned task before existing so you know it completes and has no exceptions:

private Task _proccessSmsQueueTask;
private CancellationTokenSource _cancellationTokenSource;

protected override void OnStart(string[] args)
{
    _cancellationTokenSource = new CancellationTokenSource();
    _proccessSmsQueueTask = Task.Run(() => DoWorkAsync(_cts.Token));
}

protected override void OnStop()
{
    _cancellationTokenSource.Cancel();
    try
    {
        _proccessSmsQueueTask.Wait();
    }
    catch (Exception e)
    {
        // handle exeption
    }
}
answered Nov 28 '14 at 13:36
i3arnon
38.3k1079105
    
Looks nice! How I could start the Task in this scenario under a Windows Service? – GuFigueiredo Nov 28 '14 at 13:39
    
@GuFigueiredo Start it when the service loads and wait for it when it closes. – i3arnon Nov 28 '14 at 13:41
    
The OnStop method on the WinService is not async (cannot use await). If I just cancel the token it will work or I can do this in another way? (when i use await _task, i receive: 'The await operator can only be used in a method marked with async modifier') – GuFigueiredo Nov 28 '14 at 13:50
    
@GuFigueiredo This is one of the few cases where it's ok to block synchronously on an async operation. Use Task.Wait. – i3arnon Nov 28 '14 at 13:51
    
Interesting. So, in this scenario, the delay will only run when the ProcessQueue() finishes? I don´t wanna two or more "ProccessQueues" run at same time. – GuFigueiredo Nov 28 '14 at 14:05

Sample Worker Class that I have used in Windows Services. It supports stopping in a 'clean' way by using a lock. You just have to add your code in DoWork, set your timer in the StartTimerAndWork method (in milliseconds), and use this class in your service.

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

    }
answered Nov 28 '14 at 13:39
Adrian Nasui
54818

Your Answer

 

Sign up or log in

Sign up using Google

Sign up using Facebook

Sign up using Email and Password

Post as a guest

Name
Email

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.

asked

11 months ago

viewed

1772 times

active

11 months ago

Hot Network Questions

Technology Life / Arts Culture / Recreation Science Other
  1. Stack Overflow
  2. Server Fault
  3. Super User
  4. Web Applications
  5. Ask Ubuntu
  6. Webmasters
  7. Game Development
  8. TeX - LaTeX
  1. Programmers
  2. Unix & Linux
  3. Ask Different (Apple)
  4. WordPress Development
  5. Geographic Information Systems
  6. Electrical Engineering
  7. Android Enthusiasts
  8. Information Security
  1. Database Administrators
  2. Drupal Answers
  3. SharePoint
  4. User Experience
  5. Mathematica
  6. Salesforce
  7. ExpressionEngine® Answers
  8. more (13)
  1. Photography
  2. Science Fiction & Fantasy
  3. Graphic Design
  4. Movies & TV
  5. Seasoned Advice (cooking)
  6. Home Improvement
  7. Personal Finance & Money
  8. Academia
  9. more (9)
  1. English Language & Usage
  2. Skeptics
  3. Mi Yodeya (Judaism)
  4. Travel
  5. Christianity
  6. Arqade (gaming)
  7. Bicycles
  8. Role-playing Games
  9. more (21)
  1. Mathematics
  2. Cross Validated (stats)
  3. Theoretical Computer Science
  4. Physics
  5. MathOverflow
  6. Chemistry
  7. Biology
  8. more (5)
  1. Stack Apps
  2. Meta Stack Exchange
  3. Area 51
  4. Stack Overflow Careers
site design / logo © 2015 Stack Exchange Inc; user contributions licensed under cc by-sa 3.0 with attribution required
rev 2015.11.5.2935

View static HTML