I am trying to create a Windows Service that executes a job on a timer and has graceful shutdown. I've used various questions/answers on here to come up with the code below. It works but I want to make sure it's the most correct and elegant solution. And I have specific questions too (after code).
This is the main service class.
using System;
using System.ServiceProcess;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
namespace MyService
{
public class MyService : ServiceBase
{
CancellationTokenSource cancellationTokenSource;
System.Timers.Timer serviceTimer;
Task workTask;
public static void Main(string[] args)
{
if (!Environment.UserInteractive)
{
Run(new MyService());
}
}
protected override void OnStart(string[] args)
{
cancellationTokenSource = new CancellationTokenSource();
serviceTimer = new System.Timers.Timer(30000);
serviceTimer.Elapsed += new ElapsedEventHandler(serviceTimer_Elapsed);
serviceTimer.Start();
}
protected override void OnStop()
{
try
{
serviceTimer.Stop();
cancellationTokenSource.Cancel();
if (workTask != null)
{
workTask.Wait(10000);
}
}
finally
{
serviceTimer.Dispose();
serviceTimer = null;
cancellationTokenSource.Dispose();
cancellationTokenSource = null;
}
}
private void serviceTimer_Elapsed(object sender, ElapsedEventArgs e)
{
serviceTimer.Stop();
workTask = Task.Run(() => StartWorkMethod()).ContinueWith(WorkCompleted);
}
private void WorkCompleted(Task completedTask)
{
workTask = null;
serviceTimer.Start();
}
private void StartWorkMethod()
{
Work work = new Work(cancellationTokenSource.Token);
work.StartWork();
}
}
}
This is the class that performs the (currently simulated) work.
using System.Threading;
namespace MyService
{
public class Work
{
CancellationToken cancellationToken;
public Work(CancellationToken cancellationToken)
{
this.cancellationToken = cancellationToken;
}
public void StartWork()
{
for (int i = 0; i < 4; i++)
{
if (cancellationToken.IsCancellationRequested)
{
break;
}
Thread.Sleep(10000);
}
}
}
}
The service works and runs all tasks without blocking the handler threads. If the service is stopped, the OnStop method will wait for the task's current block of work to complete for a certain period of time before stopping anyways (thanks Ian of Oz!).
Here are my specific questions:
To prevent the service from stopping immediately and waiting for the current block to complete, I am using the working variable and a while loop to wait for the Work class to complete and the bool to be set to false. Is this the best way to handle this? Already answered by Ian of Oz.
I also want to have a "feature" where if the current block is taking too long to complete, the OnStop method will only wait a certain amount of time before exiting anyways. What is the best way to implement that? Already answerd by Ian of Oz.
I've tried to make sure I handle all threading issues with my code. Is there anything I missed or that might cause trouble later with this implementation?
Also some notes to avoid any confusion:
Service install code is not included, I am using an installer to install the service.
The timer controls the time between executions so that there aren't overlapping executions if the previous execution takes longer; this is why the timer stops before starting the work and restarts after.
I've seen where the Main method is sometimes placed in it's own file, but mostly where the executable is also the installer; in this case it would only simplify this file by the Main method itself.
Edited to incorporate suggestion from Ian of Oz.
Related
I have created a Windows Service that will be calling out to some COM components, so I tagged [STAThread] to the Main function. However, when the timer fires, it reports MTA and the COM calls fail. How can I fix this?
using System;
using System.Diagnostics;
using System.ServiceProcess;
using System.Threading;
using System.Timers;
namespace MyMonitorService
{
public class MyMonitor : ServiceBase
{
#region Members
private System.Timers.Timer timer = new System.Timers.Timer();
#endregion
#region Construction
public MyMonitor ()
{
this.timer.Interval = 10000; // set for 10 seconds
this.timer.Elapsed += new System.Timers.ElapsedEventHandler(this.timer_Elapsed);
}
#endregion
private void timer_Elapsed (object sender, ElapsedEventArgs e)
{
EventLog.WriteEntry("MyMonitor", String.Format("Thread Model: {0}", Thread.CurrentThread.GetApartmentState().ToString()), EventLogEntryType.Information);
}
#region Service Start/Stop
[STAThread]
public static void Main ()
{
ServiceBase.Run(new MyMonitor());
}
protected override void OnStart (string[] args)
{
EventLog.WriteEntry("MyMonitor", "My Monitor Service Started", EventLogEntryType.Information);
this.timer.Enabled = true;
}
protected override void OnStop ()
{
EventLog.WriteEntry("MyMonitor", "My Monitor Service Stopped", EventLogEntryType.Information);
this.timer.Enabled = false;
}
#endregion
}
}
Services are run by the windows service hosting system, which runs using MTA threads. You can't control this. You have to create a new Thread and set its ApartmentState to STA, and do your work on this thread.
Here's a class that extends ServiceBase that does this:
public partial class Service1 : ServiceBase
{
private System.Timers.Timer timer;
public Service1()
{
InitializeComponent();
timer = new System.Timers.Timer();
this.timer.Interval = 10000; // set for 10 seconds
this.timer.Elapsed += new System.Timers.ElapsedEventHandler(Tick);
}
protected override void OnStart(string[] args)
{
timer.Start();
}
private void Tick(object sender, ElapsedEventArgs e)
{
// create a thread, give it the worker, let it go
// is collected when done (not IDisposable)
var thread = new Thread(WorkerMethod);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
OnStop(); // kill the timer
}
private void WorkerMethod(object state)
{
// do your work here in an STA thread
}
protected override void OnStop()
{
timer.Stop();
timer.Dispose();
}
}
Note this code doesn't actually stop the service, it stops the timer. There could be lots of work still being done on multiple threads. For instance, if your work consisted of running multiple queries off a large database you may end up crashing because you have too many threads running at the same time.
In a situation like this, I'd create a set number of STA threads (maybe 2x the number of cores to start off with) which monitor a thread-safe queue for work items. The timer tick event would be responsible for loading that queue with the work needing done.
It all depends on what you're actually doing every ten seconds, whether or not it should be completed the next time the timer ticks, what you should do in this situation, etc etc.
That cannot work in a service, the thread that calls your Main() method was already started by the service manager. You'll need to create a separate thread that is initialized with Thread.SetApartmentState() and pumps a message loop.
Setting the STAThread attribute will not work on a service. It's not being handled the same way as an application, so this will get ignored.
My recommendation would be to manually make a separate thread for your service, set its apartment state, and move everything into it. This way, you can set the thread to STA correctly.
However, there will be another issue here - you'll have to rework the way your service works. You can't just use a System.Threading.Timer instance for timing - it runs on a separate thread, which will not be STA. When its elapsed event fires, you'll be working on a different, non-STA thread.
Instead of doing your work in the timer event, you'll probably want to do your main work in the thread you create explicitly. You can have a reset event in that thread which blocks, and have your timer "set" it to allow your logic to run in the STA thread.
Looking at a similar example: http://www.aspfree.com/c/a/C-Sharp/Creating-a-Windows-Service-with-C-Sharp-introduction/1/
What if your main is...
[STAThread]
public static void Main ()
{
MyMonitor m = new MyMonitor();
m.Start();
}
and move your timer start / stop out of the events...
public void Start() { this.timer.Enabled = true;}
public void Stop() { this.timer.Enabled = false;}
protected override void OnStart (string[] args)
{
EventLog.WriteEntry("MyMonitor", "My Monitor Service Started", EventLogEntryType.Information);
}
protected override void OnStop ()
{
EventLog.WriteEntry("MyMonitor", "My Monitor Service Stopped", EventLogEntryType.Information);
}
This reports that it is using STA. It is based on Will's suggestion and http://en.csharp-online.net/Creating_a_.NET_Windows_Service%E2%80%94Alternative_1:_Use_a_Separate_Thread
using System;
using System.Diagnostics;
using System.ServiceProcess;
using System.Threading;
namespace MyMonitorService
{
internal class MyMonitorThreaded : ServiceBase
{
private Boolean bServiceStarted = false;
private Thread threadWorker;
private void WorkLoop ()
{
while (this.bServiceStarted)
{
EventLog.WriteEntry("MyMonitor", String.Format("Thread Model: {0}", Thread.CurrentThread.GetApartmentState().ToString()), EventLogEntryType.Information);
if (this.bServiceStarted)
Thread.Sleep(new TimeSpan(0, 0, 10));
}
Thread.CurrentThread.Abort();
}
#region Service Start/Stop
protected override void OnStart (String[] args)
{
this.threadWorker = new Thread(WorkLoop);
this.threadWorker.SetApartmentState(ApartmentState.STA);
this.bServiceStarted = true;
this.threadWorker.Start();
}
protected override void OnStop ()
{
this.bServiceStarted = false;
this.threadWorker.Join(new TimeSpan(0, 2, 0));
}
#endregion
}
}
I have developed my first C# Service in VS 2015, but I cannot get my ElapsedEventHandler method to fire. I have the following code:
using System;
using System.ServiceProcess;
using System.Timers;
namespace UpdateEnvironmentService
{
public partial class Scheduler : ServiceBase
{
private Timer timer = null;
public Scheduler()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
timer = new Timer();
this.timer.Interval = Convert.ToDouble(1000); //timer intraval in milliseconds
this.timer.Elapsed += new System.Timers.ElapsedEventHandler(this.UpdateData);
timer.Enabled = true;
Library.WriteLog("Data Updater Started ");
}
private void UpdateData(object sender, EventArgs e)
{
Library.WriteLog("Got to update Data ");
}
protected override void OnStop()
{
timer.Enabled = false;
timer = null;
Library.WriteLog("Data Updater Stopped ");
}
}
}
The line Data Updater Started gets printed to my log file, but I never end up seeing Got to update Data or even Data Updater Stopped. It seems my ElapsedEventHandler is never firing. Anybody have any idea why?
I would refer you to the documentation on MSDN for the System.Timers.Timer class.
Most examples and usage of the timer, tends to avoid setting Enabled directly and instead relies on the Start and Stop methods.
On a side-note, I would recommend that in the age of Task, that you approach the problem in a different way:
namespace UpdateEnvironmentService
{
public partial class Scheduler : ServiceBase
{
private readonly CancellationTokenSource _tcs;
public Scheduler()
{
InitializeComponent();
_tcs = new CancellationTokenSource();
}
protected override void OnStart(string[] args)
{
Library.WriteLog("Data Updater Started ");
Task.Factory.StartNew(Runner, _tcs.Token);
}
private async void Runner()
{
Library.WriteLog("In runner");
var delay = TimeSpan.FromSeconds(1);
while(!_tcs.IsCancellationRequested)
{
Library.WriteLog("Waiting...");
await Task.Delay(delay, _tcs.Token);
UpdateData();
}
Library.WriteLog("Cancellation requested; exiting runner");
}
private void UpdateData()
{
Library.WriteLog("Got to update Data ");
}
protected override void OnStop()
{
_tcs.Cancel();
Library.WriteLog("Data Updater Stopped ");
}
}
}
This approach removes the need for a timer, and instead introduces asynchrony from the task, allowing the threadpool to manage the delay; it also has the benefit of introducing nicer cancellation control, which means it can even be cancelled while it's waiting for the delay!
I've found a couple of Stack Overflow questions along with a couple of blog posts that already touch on this topic, but unfortunately none of them are meeting my needs. I'll just start with some sample code to show what I'd like to accomplish.
using System;
using System.Security.Permissions;
using System.Threading.Tasks;
using System.Windows.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace MyApp
{
[TestClass]
public class MyTests
{
private int _value;
[TestMethod]
public async Task TimerTest()
{
_value = 0;
var timer = new DispatcherTimer {Interval = TimeSpan.FromMilliseconds(10)};
timer.Tick += IncrementValue;
timer.Start();
await Task.Delay(15);
DispatcherUtils.DoEvents();
Assert.AreNotEqual(0, _value);
}
private void IncrementValue(object sender, EventArgs e)
{
_value++;
}
}
internal class DispatcherUtils
{
[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public static void DoEvents()
{
var frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(ExitFrame), frame);
Dispatcher.PushFrame(frame);
}
private static object ExitFrame(object frame)
{
((DispatcherFrame)frame).Continue = false;
return null;
}
}
}
This code works fine if, instead of using a DispatcherTimer, I use an ordinary Timer. But DispatcherTimer never fires. What am I missing? What do I need to get it to fire?
It's best if you can avoid DispatcherTimer in your system under test and use an abstraction instead (Rx has a nice one called IScheduler). This kind of abstraction allows you to explicitly control the flow of time in your unit tests, rather than making your tests conditional on CPU timings.
But if you are just interested in unit testing for now, then you'll need to create an STA thread that does message pumping and have a proper Dispatcher installed. All "run this code on the dispatcher" operations just wrap a delegate in a Win32 message, and if you don't have a Win32 message pumping loop in a Dispatcher (before creating the timer), then those messages won't be processed.
The easiest way to do this is to use WpfContext from here:
[TestMethod]
public async Task TimerTest()
{
await WpfContext.Run(() =>
{
_value = 0;
var timer = new DispatcherTimer {Interval = TimeSpan.FromMilliseconds(10)};
timer.Tick += IncrementValue;
timer.Start();
await Task.Delay(15);
Assert.AreNotEqual(0, _value);
});
}
Again, this kind of approach is substandard because it depends on timings. So if your antivirus gets upset and decides to inspect your unit test, it can spuriously fail. An abstraction like IScheduler enables reliable unit tests.
There's plenty of examples of people saying to use a Timer instead of Thread.Sleep(...) in an Azure Worker Role. No probs with that.
What I'm struggling to understand is how to code this.
Currently, I have the following (pseduo code)
_timer.Elapsed += (sender, args) => DoWork();
public override void Run()
{
while(true)
{
DoWork();
}
}
public void DoWork()
{
try
{
_timer.Stop();
// Now - do stuff ....
}
catch(....) { ... }
_timer.Start()
}
And what happens, is that the code enters the DoWork() method once and DoesStuff(tm).. fine .. starts the timer (say .. with a 30 second interval) and then exits that method.
Then, it returns back to the main Run() method .. which is in that loop. So it immediately comes back around and enters the DoWork() method again .. instead of waiting for the timer to fire it off.
So I'm not sure how to replace any Thread.Sleep(...) with Timers.
Any clues?
Clarification
I do not want to exit the Run() method :) I'm very happy to keep looping forever. What I'm stuck with, is replacing the standard Thread.Sleep(...) call (which blocks the thread) and replace that with a Timer, which most people suggest.
Update
Please do not link or suggest that I should use cancelSource.Token.WaitHandle.WaitOne(); as a solution. That is not what I'm trying to achieve here. Please note the post title!
I figure that if you want to solve this situation the way you outline here you will need a WaitHandle AND a Timer.
The short answer is here below. The long answer became a blog post: HowTo wait in a WorkerRole using Timer and EventWaitHandle over Thread.Sleep
I used an EventWaitHandle along with the Timer and came up with this solution:
public class WorkerRole : RoleEntryPoint
{
Waiter waiter;
public override bool OnStart()
{
waiter = new Waiter(WorkerConfiguration.WaitInterval);
return base.OnStart();
}
public override void Run()
{
while (true)
{
DoWork();
waiter.Wait();
}
}
public void DoWork()
{
// [...]
}
}
And here is the waiter class:
public class Waiter
{
private readonly Timer timer;
private readonly EventWaitHandle waitHandle;
public Waiter(TimeSpan? interval = null)
{
waitHandle = new AutoResetEvent(false);
timer = new Timer();
timer.Elapsed += (sender, args) => waitHandle.Set();
SetInterval(interval);
}
public TimeSpan Interval
{
set { timer.Interval = value.TotalMilliseconds; }
}
public void Wait(TimeSpan? newInterval = null)
{
SetInterval(newInterval);
timer.Start();
waitHandle.WaitOne();
timer.Close();
waitHandle.Reset();
}
private void SetInterval(TimeSpan? newInterval)
{
if (newInterval.HasValue)
{
Interval = newInterval.Value;
}
}
}
I'm having a small background thread which runs for the applications lifetime - however when the application is shutdown, the thread should exit gracefully.
The problem is that the thread runs some code at an interval of 15 minutes - which means it sleeps ALOT.
Now in order to get it out of sleep, I toss an interrupt at it - my question is however, if there's a better approach to this, since interrupts generate ThreadInterruptedException.
Here's the gist of my code (somewhat pseudo):
public class BackgroundUpdater : IDisposable
{
private Thread myThread;
private const int intervalTime = 900000; // 15 minutes
public void Dispose()
{
myThread.Interrupt();
}
public void Start()
{
myThread = new Thread(ThreadedWork);
myThread.IsBackground = true; // To ensure against app waiting for thread to exit
myThread.Priority = ThreadPriority.BelowNormal;
myThread.Start();
}
private void ThreadedWork()
{
try
{
while (true)
{
Thread.Sleep(900000); // 15 minutes
DoWork();
}
}
catch (ThreadInterruptedException)
{
}
}
}
There's absolutely a better way - either use Monitor.Wait/Pulse instead of Sleep/Interrupt, or use an Auto/ManualResetEvent. (You'd probably want a ManualResetEvent in this case.)
Personally I'm a Wait/Pulse fan, probably due to it being like Java's wait()/notify() mechanism. However, there are definitely times where reset events are more useful.
Your code would look something like this:
private readonly object padlock = new object();
private volatile bool stopping = false;
public void Stop() // Could make this Dispose if you want
{
stopping = true;
lock (padlock)
{
Monitor.Pulse(padlock);
}
}
private void ThreadedWork()
{
while (!stopping)
{
DoWork();
lock (padlock)
{
Monitor.Wait(padlock, TimeSpan.FromMinutes(15));
}
}
}
For more details, see my threading tutorial, in particular the pages on deadlocks, waiting and pulsing, the page on wait handles. Joe Albahari also has a tutorial which covers the same topics and compares them.
I haven't looked in detail yet, but I wouldn't be surprised if Parallel Extensions also had some functionality to make this easier.
You could use an Event to Check if the Process should end like this:
var eventX = new AutoResetEvent(false);
while (true)
{
if(eventX.WaitOne(900000, false))
{
break;
}
DoWork();
}
There is CancellationTokenSource class in .NET 4 and later which simplifies this task a bit.
private readonly CancellationTokenSource cancellationTokenSource =
new CancellationTokenSource();
private void Run()
{
while (!cancellationTokenSource.IsCancellationRequested)
{
DoWork();
cancellationTokenSource.Token.WaitHandle.WaitOne(
TimeSpan.FromMinutes(15));
}
}
public void Stop()
{
cancellationTokenSource.Cancel();
}
Don't forget that CancellationTokenSource is disposable, so make sure you dispose it properly.
One method might be to add a cancel event or delegate that the thread will subscribe to. When the cancel event is invoke, the thread can stop itself.
I absolutely like Jon Skeets answer. However, this might be a bit easier to understand and should also work:
public class BackgroundTask : IDisposable
{
private readonly CancellationTokenSource cancellationTokenSource;
private bool stop;
public BackgroundTask()
{
this.cancellationTokenSource = new CancellationTokenSource();
this.stop = false;
}
public void Stop()
{
this.stop = true;
this.cancellationTokenSource.Cancel();
}
public void Dispose()
{
this.cancellationTokenSource.Dispose();
}
private void ThreadedWork(object state)
{
using (var syncHandle = new ManualResetEventSlim())
{
while (!this.stop)
{
syncHandle.Wait(TimeSpan.FromMinutes(15), this.cancellationTokenSource.Token);
if (!this.cancellationTokenSource.IsCancellationRequested)
{
// DoWork();
}
}
}
}
}
Or, including waiting for the background task to actually have stopped (in this case, Dispose must be invoked by other thread than the one the background thread is running on, and of course this is not perfect code, it requires the worker thread to actually have started):
using System;
using System.Threading;
public class BackgroundTask : IDisposable
{
private readonly ManualResetEventSlim threadedWorkEndSyncHandle;
private readonly CancellationTokenSource cancellationTokenSource;
private bool stop;
public BackgroundTask()
{
this.threadedWorkEndSyncHandle = new ManualResetEventSlim();
this.cancellationTokenSource = new CancellationTokenSource();
this.stop = false;
}
public void Dispose()
{
this.stop = true;
this.cancellationTokenSource.Cancel();
this.threadedWorkEndSyncHandle.Wait();
this.cancellationTokenSource.Dispose();
this.threadedWorkEndSyncHandle.Dispose();
}
private void ThreadedWork(object state)
{
try
{
using (var syncHandle = new ManualResetEventSlim())
{
while (!this.stop)
{
syncHandle.Wait(TimeSpan.FromMinutes(15), this.cancellationTokenSource.Token);
if (!this.cancellationTokenSource.IsCancellationRequested)
{
// DoWork();
}
}
}
}
finally
{
this.threadedWorkEndSyncHandle.Set();
}
}
}
If you see any flaws and disadvantages over Jon Skeets solution i'd like to hear them as i always enjoy learning ;-)
I guess this is slower and uses more memory and should thus not be used in a large scale and short timeframe. Any other?