Blog Stats
  • Posts - 24
  • Articles - 0
  • Comments - 15
  • Trackbacks - 0

 

A better way for service threads

Every time I inherit a windows service, I see the same design pattern:

 

public void ServiceProcess()
        {
            while (true)
            {
               Foo();
               Thread.Sleep(300000);
            }
        }

 

The theory here is that we are processing a work queue which will receive items, and every five minutes we will check to see what work has arrived in the queue.  Do work, sleep five minutes, Do work, repeat.  The problem is that we are blocking the owning process thread for five minutes while we sleep.  If there is a request to stop the work thread while sleeping, we have no way to wake up the sleeping thread.  This can result in a thread abort exception, and if we try to start a new thread we have strange behavior where the initiating thread can collide with the thread that is currently running.  Instead, use a timer to handle sleeping -

 

System.Threading.Timer _timer1;
           
        public void ServiceProcess()
        {
            System.Threading.TimerCallback tcb = new System.Threading.TimerCallback(Foo);

            _timer1 = new Timer(tcb, null, 0, 300000); // Start immediately, wait 5 minutes between executions
        }

 private void Foo(object stateInfo); // Method signature must take state object which is then parsed

 

To stop the process from executing, update _timer1 to prevent the callback from executing. 

_timer1.Change(System.Threading.Timeout.Infinite,
                System.Threading.Timeout.Infinite);

Likewise if we want to have more control over when the process executes, we can directly modify _timer1 to start the process immediately.  Much cleaner.

 


Feedback

# re: A better way for service threads

Gravatar This is very important stuff for implementation of windows services. A common gotcha here is that developers often drag-n-drop the wrong timer control onto the component tray of the service.

Be sure to use the Timer from System.Threading.Timer (as Jeff does above)and NOT from System.Windows.Forms.Timer as the latter is not suited for multi-threaded / MTA environments like with the Windows Service. 9/9/2010 2:29 AM | Jaans

# re: A better way for service threads

Gravatar What's wrong with a WaitHandle that gets signaled both when shutdown has been requested or an item is added to the queue?

Agree with your larger point though - Using Thread.Sleep is a strong no-hire signal. 9/9/2010 3:12 AM | James

# re: A better way for service threads

Gravatar Also, if it wasn't clear, replacing Thread.Sleep with WaitHandle.WaitOne() which means effectively zero lag between an action occurring that the thread needs to handle, and the thread waking up to handle that action. 9/9/2010 3:14 AM | James

# re: A better way for service threads

Gravatar I prefer using events (Ex: using FilesystemWatchter) instead of timers.

If using timers:

* The duration of Foo() may be longer than the timer interval.
Thus I do restart the timer at the end of Foo() instead of using an interval.

* The proccessing in Foo() should check for stop requests to enhance process managebility. Otherwise the SCM might kill the service on shutdown instead of gracefully shutting it down.

Example:

Private _pollTimer as new Threading.Timer(Sub() Foo())
Private _isStopRequested As New ManualResetEvent(False)
Const _pollInterval = 300 * 1000

Sub OnStart()
_pollTimer.Change(0, Timeout.Infinite)
...
End Sub

Sub Foo()
If _isStopRequested.WaitOne(0) Then Exit Sub
Dim files = GetAllFilesSortedByLastAccess(WatchPath, FileNameFilter)
Do While files.Count > 0 And Not _isStopRequested.WaitOne(0)
For Each fileinfo In files
...
Next
files = GetAllFilesSortedByLastAccess(WatchPath, FileNameFilter)
Loop
_pollTimer.Change(_pollInterval, Timeout.Infinite)
End Sub

Sub OnStop()
_isStopRequested.Set()
_pollTimer.Dispose
End Sub



9/9/2010 6:44 AM | Peter Meinl

# A better solution

Gravatar Instead of using timers, use ThreadPool.RegisterWaitForSingleObject because it utilizes IOCP threads.

Read about it here -

http://blogs.msdn.com/b/morgan/archive/2008/12/18/periodic-execution-in-net.aspx 9/9/2010 10:34 AM | Wayne B

Post A Comment
Title:
Name:
Email:
Comment:
Verification:
 
 

 

 

Copyright © jkrebsbach