How to capture Exceptions from threads I have no direct access to? - c#

I have developed a Windows Service capable of running a few plugins. Due to its nature, when developing Windows Services, the Start and Stop methods should run and return as fast as possible. The Start method runs Start methods from all plugins, which also should not block the execution. In this example, both plugins instantiate a Threading.Timer, which run in background.
The execution order happens as follows. The arrows indicate what runs in a different thread:
-> MyService.Start -> pA.Start -> pb.Start -> return
\_> DoWork() \
\_> DoWork()
Since both DoWork() are running inside a Timer, if an Exception happens, I am unable to catch it. This could easily be avoided if I could modify PluginA and PluginB, but I can't.
Any suggestion on what I could do to avoid this issue? Thanks in advance.
The following code is an oversimplification of the real code:
public class MyService
{
private PluginA pA = new PluginA();
private PluginB pB = new PluginB();
// Windows Service runs Start when the service starts. It must return ASAP
public void Start()
{
// try..catch doesn't capture PluginB's exception
pA.Start();
pB.Start();
}
// Windows Service runs Stop when the service Stops. It must return ASAP
public void Stop()
{
pA.Stop();
pB.Stop();
}
}
// I have no control over how this is developed
public class PluginA
{
private Timer _timer;
public void Start()
{
_timer = new Timer(
(e) => DoWork(),
null,
TimeSpan.Zero,
TimeSpan.FromSeconds(10));
}
private void DoWork()
{
File.AppendAllText(
"C:/log.txt",
"hello" + Environment.NewLine);
}
public void Stop()
{
_timer.Change(Timeout.Infinite, 0);
}
}
// I have no control over how this is developed
public class PluginB
{
private Timer _timer;
public void Start()
{
_timer = new Timer(
(e) => DoWork(),
null,
TimeSpan.Zero,
TimeSpan.FromSeconds(10));
}
private void DoWork()
{
File.AppendAllText(
"C:/log.txt",
"Goodbye" + Environment.NewLine);
throw new Exception("Goodbye");
}
public void Stop()
{
_timer.Change(Timeout.Infinite, 0);
}
}

You can also use the AppDomain.UnhandledException Event.
Please note that you can't recover from such an exception.

Related

How to run a method in the background only when app is open and running?

Once the app is open and running I would like a background process to check a database and to make an update depending on the data in the database. I would like to make this check every one minute. I only want this to happen when the app is in the foreground and in view of the user.
Can someone give me some suggestions as to how I do this? I assume I can call a method from here but I'm not sure how to do this. Also I do not know how to stop or even if I need to manually cancel / stop the process. Would it cancel itself when the app is not in the foreground and restart when the app came back into the foreground?
public partial class App : Application
{
protected override void OnStart()
{
App.DB.InitData();
MainPage = new Japanese.MainPage();
}
But do I need to make this run on a different thread and if so how could I do that.
Sorry if my question is not clear. Please ask and I can update if it doesn't make sense.
What we did in our forms application was to make use of the Device.Timer and the Stopwatch class that available in System.Diagnostics, and Xamarin.Forms to create a very generic managed timer that we could interact with using the onStart, onSleep and onResume methods in Xamarin.Forms.
This particular solution doesn't require any special platform specific logic, and the device timer and stopwatch are non UI blocking.
using Xamarin.Forms;
using System;
using System.Linq;
using System.Diagnostics;
namespace YourNamespace
{
public partial class App : Application
{
private static Stopwatch stopWatch = new Stopwatch();
private const int defaultTimespan = 1;
protected override void OnStart()
{
// On start runs when your application launches from a closed state,
if (!stopWatch.IsRunning)
{
stopWatch.Start();
}
Device.StartTimer(new TimeSpan(0, 0, 1), () =>
{
// Logic for logging out if the device is inactive for a period of time.
if (stopWatch.IsRunning && stopWatch.Elapsed.Minutes >= defaultTimespan)
{
//prepare to perform your data pull here as we have hit the 1 minute mark
// Perform your long running operations here.
Device.InvokeOnMainThread(()=>{
// If you need to do anything with your UI, you need to wrap it in this.
});
stopwatch.Restart();
}
// Always return true as to keep our device timer running.
return true;
});
}
protected override void OnSleep()
{
// Ensure our stopwatch is reset so the elapsed time is 0.
stopWatch.Reset();
}
protected override void OnResume()
{
// App enters the foreground so start our stopwatch again.
stopWatch.Start();
}
}
}
Edit:
To give some context as to how the above solution works step by step:
The application starts from a closed state and the 'OnStart()' method creates our Device.Timer that ticks every second. It also starts our stopwatch that counts upto a minute.
When the app goes into the background it hits the 'OnSleep' method at this point if we were to pass a 'false' value into our Device.StartTimer() action it would not start up again. So instead we simply reset our stopwatch ready for when the app is opened again.
When the app comes back into the foreground it hits the 'OnResume' method, which simply starts the existing stopwatch.
2018 Edit:
This answer still has some merits even in 2018, but mainly for very specific situations. There are better platform specific ways to replicate this functionality even in Xamarin.Forms. The above still remains a platform agnostic way to perform a task after a period of time, taking into account user activity/inactivity.
you can use this,
System.Threading.Tasks.Task.Run(() =>
{
//Add your code here.
}).ConfigureAwait(false);
There are a few ways to do this in both iOS and Android. In Xamarin Forms most of this functionality falls under the moniker of Backgrounding. There are a lot of tutorials out there. This one is quite elaborate and definitely worth checking out:
http://arteksoftware.com/backgrounding-with-xamarin-forms/
In Android a lot of this work is done in a Background Service. For iOS look into Long Running or Finite-Length Tasks. As you can tell from this there is no Xamarin Forms way of doing this. You will need to write Xamarin.Android and Xamarin.iOS specific code.
To run a background task use a Service. Generally classifies tasks as either Long Running Tasks or Periodic Tasks.
The code for service in android look like this
[Service]
public class PeriodicService : Service
{
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
// From shared code or in your PCL
return StartCommandResult.NotSticky;
}
}
And to invoke the service in background
var intent = new Intent (this, typeof(PeriodicService));
StartService(intent);
In case wants to invoke and check after every minute
private void StartBackgroundDataRefreshService ()
{
var pt = new PeriodicTask.Builder ()
.SetPeriod (1800) // in seconds; minimum is 30 seconds
.SetService (Java.Lang.Class.FromType (typeof(BackgroundService)))
.SetRequiredNetwork (0)
.SetTag (your package name) // package name
.Build ();
GcmNetworkManager.GetInstance (this).Schedule (pt);
}
In order to know which service type is good for you read this tutorial
Types of Services
Xamarin Blog for periodic background service
Xamarin Service Blog
The other example is
public class PeriodicService : Service
{
private static Timer timer = new Timer();
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
timer.scheduleAtFixedRate(new mainTask(), 0, 5000);
return StartCommandResult.NotSticky;
}
private class mainTask extends TimerTask
{
public void run()
{
//your code
}
}
}
Here is Sample Code of XAMARIN Android Service Which will perform task after every 10 Seconds
using System;
using System.Threading;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Util;
namespace SimpleService
{
[Service]
public class SimpleStartedService : Service
{
static readonly string TAG = "X:" + typeof(SimpleStartedService).Name;
static readonly int TimerWait = 10000;
Timer timer;
DateTime startTime;
bool isStarted = false;
public override void OnCreate()
{
base.OnCreate();
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
Log.Debug(TAG, $"OnStartCommand called at {startTime}, flags={flags}, startid={startId}");
if (isStarted)
{
TimeSpan runtime = DateTime.UtcNow.Subtract(startTime);
Log.Debug(TAG, $"This service was already started, it's been running for {runtime:c}.");
}
else
{
startTime = DateTime.UtcNow;
Log.Debug(TAG, $"Starting the service, at {startTime}.");
timer = new Timer(HandleTimerCallback, startTime, 0, TimerWait);
isStarted = true;
}
return StartCommandResult.NotSticky;
}
public override IBinder OnBind(Intent intent)
{
// This is a started service, not a bound service, so we just return null.
return null;
}
public override void OnDestroy()
{
timer.Dispose();
timer = null;
isStarted = false;
TimeSpan runtime = DateTime.UtcNow.Subtract(startTime);
Log.Debug(TAG, $"Simple Service destroyed at {DateTime.UtcNow} after running for {runtime:c}.");
base.OnDestroy();
}
void HandleTimerCallback(object state)
{
TimeSpan runTime = DateTime.UtcNow.Subtract(startTime);
Log.Debug(TAG, $"This service has been running for {runTime:c} (since ${state})." );
}
}
}
You can use
Device.StartTimer(TimeSpan.FromMinutes(1), () =>
{
var shouldTimerContinueWork = true;
/*your code*/
return shouldTimerContinueWork;
});
This timer runs on background thread, uses devices clock and reentrance safe.
To stop this timer when app is in background you can use Xamarin.Forms.Application methods OnSleep and OnResume as described here
I'm doing something like this is my Xamarin Forms apps.
public void execute()
{
var thread = new Thread(new ThreadStart(startAuthenticationProcess))
{
IsBackground = true
};
thread.Start();
}
private void startAuthenticationProcess()
{
Thread.Sleep(2000);
if (!Utils.isNetworkAvailable(splashActivity))
{
splashActivity.RunOnUiThread(() => Utils.showToast(splashActivity, splashActivity.GetString(Resource.String.r30025)));
splashActivity.FinishAffinity();
}
else
{
try
{
if (StringUtils.isBlank(strIPAdd) || (StringUtils.isNotBlank(strIPAdd) && (StringUtils.isBlank(strDbName) || "site".Equals(strDbName,StringComparison.OrdinalIgnoreCase))))
{
splashActivity.RunOnUiThread(() => DependencyService.Get<IAuthenticationDialog>().showAuthenticationDialog(new Command(() =>
{
var intent = new Intent(splashActivity, typeof(MainActivity));
intent.PutExtra("startLoginActivity", false);
splashActivity.StartActivity(intent);
splashActivity.Finish();
})));
}
else
{
gotoLoginScreen();
}
}
catch (Exception e)
{
Log.Error(TAG, e.Message);
}
}
}
Easy, try something like this and implement your logic in those methods:
public partial class App : Application
{
protected override void OnStart()
{
// Your App On start code should be here...
// and then:
Task.Run(() =>
{
//Add your code here, it might looks like:
CheckDatabase();
MakeAnUpdateDependingOnDatabase();
});
}
I hope it helps.

C# wait timeout before calling method and reset timer on consecutive calls

I have a event in my code that can possibly get fired multiple times a second at some moment.
However I would like to implement a way to make that method wait 500ms before really firing, if the method gets called again before those 500ms are over, reset the timer and wait for 500ms again.
Coming from javascript I know this is possible with setTimeout or setInterval. However I'm having trouble figuring out how I could implement such a thing in C#.
You could use a System.Timers.Timer wrapped in a class to get the behaviour you need:
public class DelayedMethodCaller
{
int _delay;
Timer _timer = new Timer();
public DelayedMethodCaller(int delay)
{
_delay = delay;
}
public void CallMethod(Action action)
{
if (!_timer.Enabled)
{
_timer = new Timer(_delay)
{
AutoReset = false
};
_timer.Elapsed += (object sender, ElapsedEventArgs e) =>
{
action();
};
_timer.Start();
}
else
{
_timer.Stop();
_timer.Start();
}
}
}
This can then be used in the following manner:
public class Program
{
static void HelloWorld(int i)
{
Console.WriteLine("Hello World! " + i);
}
public static void Main(string[] args)
{
DelayedMethodCaller methodCaller = new DelayedMethodCaller(500);
methodCaller.CallMethod(() => HelloWorld(123));
methodCaller.CallMethod(() => HelloWorld(123));
while (true)
;
}
}
If you run the example, you will note that "Hello World! 123" is only displayed once - the second call simply resets the timer.
If you need to reset the timer when the method is called again, consider looking at the ManualResetEvent class:
https://msdn.microsoft.com/en-us/library/system.threading.manualresetevent(v=vs.110).aspx
You can use this to notify one or more waiting threads that an event has occurred.
You can use Thread.Sleep() with locking
private object locking = new object();
lock (locking )
{
Thread.Sleep(500);
//Your code to run here
}
https://msdn.microsoft.com/en-us/library/system.threading.thread.sleep(v=vs.110).aspx
Just writen super simple class with System.Threading.Thread; With a little different approach Usage.
var delayedCaller = new DelayedTimeout(() => HelloWorld(123), 500, false);
delayedCaller.ResetTimer();
delayedCaller.ResetTimer();
Currently, you can do it very simple with the following class
public class DelayedTimeout
{
readonly Timer _timer;
readonly int _timeoutMs;
public DelayedTimeout(TimerCallback callback, int timeoutMs, bool startNow)
{
_timeoutMs = timeoutMs;
// Should we start now
var currentTimeoutMs = startNow ? _timeoutMs : Timeout.Infinite;
_timer = new Timer(callback, null, currentTimeoutMs, Timeout.Infinite);
}
// Constructor overloading
public DelayedTimeout(Action callback, int timeoutMs, bool startNow) :
this(delegate (object? obj) { callback.Invoke(); }, timeoutMs, startNow)
{}
public void ResetTimer()
{
_timer.Change(Timeout.Infinite, Timeout.Infinite); // Stop the timer
_timer.Change(_timeoutMs, Timeout.Infinite); // Stop the timer
}
}

Why Windows.System.Threading.ThreadPoolTimer.Cancel() doesn't work

UPDATE: This works in Windows 10 properly.
Here is a simple example:
void testcase()
{
if (myTimer != null)
myTimer.Cancel();
myTimer = ThreadPoolTimer.CreateTimer(
t => myMethod(),
TimeSpan.FromMilliseconds(4000)
);
}
void myMethod()
{
myTimer = null;
//some work
}
What it should do is ensure that myMethod cannot be called more frequent than once in 4s and that myMethod shouldn't be called if there is already a newer call to testcase. Something similar with .net timers on desktop was possible. However, new call to testcase doesn't prevent previously scheduled myMethods from running. I have a simple workaround by adding integer callid parameter to myMethod and keeping track of it. But this above should work and it doesn't.
Am I doing something wrong? Does anyone have also any better idea on how to do this?
What you're looking for is called debouncing, at least in javascript.
A simple way to achieve it is to use the System.Threading.Timer instead, which has a handy Change used to reset it.
If you want to abstract it into your own timer class, it would look something like:
public class DebounceTimer : IDisposable
{
private readonly System.Threading.Timer _timer;
private readonly int _delayInMs;
public DebounceTimer(Action callback, int delayInMs)
{
_delayInMs = delayInMs;
// the timer is initially stopped
_timer = new System.Threading.Timer(
callback: _ => callback(),
state: null,
dueTime: System.Threading.Timeout.Infinite,
period: System.Threading.Timeout.Infinite);
}
public void Reset()
{
// each call to Reset() resets the timer
_timer.Change(
dueTime: _delayInMs,
period: System.Threading.Timeout.Infinite);
}
public void Dispose()
{
// timers should be disposed when you're done using them
_timer.Dispose();
}
}
Your test case would then become:
private DebounceTimer _timer;
void Init()
{
// myMethod will be called 4000ms after the
// last call to _timer.Reset()
_timer = new DebounceTimer(myMethod, 4000);
}
void testcase()
{
_timer.Reset();
}
void myMethod()
{
//some work
}
public void Dispose()
{
// don't forget to cleanup when you're finished testing
_timer.Dispose();
}
[Update]
From your comments, it seems like you'd like to change the callback method with each reset, and only have the last one invoked. If that's the case, you can change the code to something like:
class DebounceTimer : IDisposable
{
private readonly System.Threading.Timer _timer;
private readonly int _delayInMs;
private Action _lastCallback = () => { };
public DebounceTimer(int delayInMs)
{
_delayInMs = delayInMs;
// the timer is initially stopped
_timer = new System.Threading.Timer(
callback: _ => _lastCallback(),
state: null,
dueTime: System.Threading.Timeout.Infinite,
period: System.Threading.Timeout.Infinite);
}
public void Reset(Action callback)
{
_timer.Change(dueTime: _delayInMs, period: System.Threading.Timeout.Infinite);
// note: no thread synchronization is taken into account here,
// a race condition might occur where the same callback would
// be executed twice
_lastCallback = callback;
}
public void Dispose()
{
_timer.Dispose();
}
}
When calling the Reset method, you can use a lambda to capture various method calls (not only Action methods):
void testcase()
{
_timer.Reset(() => myMethod());
}
void othertestcase()
{
// it's still a parameterless action, but it
// calls another method with two parameters
_timer.Reset(() => someOtherMethod(x, y));
}
As stated in the comments for the second timer snippet, the code is not thread safe, because the timer handler may already be running (or just about to run) on a separate thread while the callback reference is being changed inside the Reset method, meaning that the same callback would be executed twice.
A slightly more complex solution would be to lock while changing the callback, and make an additional check if enough time has elapsed since the last call to reset. The final code would then look like this (there might be other ways to synchronize, but this one is pretty straightforward imho):
class DebounceTimer : IDisposable
{
private readonly System.Threading.Timer _timer;
private readonly int _delayInMs;
private readonly object _lock = new object();
private DateTime _lastResetTime = DateTime.MinValue;
private Action _lastCallback = () => { };
public DebounceTimer(int delayInMs)
{
_delayInMs = delayInMs;
// the timer is initially stopped
_timer = new System.Threading.Timer(
callback: _ => InvokeIfTimeElapsed(),
state: null,
dueTime: System.Threading.Timeout.Infinite,
period: System.Threading.Timeout.Infinite);
}
private void InvokeIfTimeElapsed()
{
Action callback;
lock (_lock)
{
// if reset just happened, skip the whole thing
if ((DateTime.UtcNow - _lastResetTime).TotalMilliseconds < _delayInMs)
return;
else
callback = _lastCallback;
}
// if we're here, we are sure we've got the right callback - invoke it.
// (even if reset happens now, we captured the previous callback
// inside the lock)
callback();
}
public void Reset(Action callback)
{
lock (_lock)
{
// reset timer
_timer.Change(
dueTime: _delayInMs,
period: System.Threading.Timeout.Infinite);
// save last reset timestamp
_lastResetTime = DateTime.UtcNow;
// set the new callback
_lastCallback = callback;
}
}
public void Dispose()
{
_timer.Dispose();
}
}
The problem is that you are setting timer = null in myMethod. That guarantees that it will be null in the next call to testCase (so it won't be cancelled).
Instead, use TimerPool.CreateTimer to create a single-instance timer. It will only fire once. When your worker process finishes, the last thing it should do is initialize a new timer.
To answer my self what is likely the problem, it seems that Cancel() is used only to cancel periodic timer from further repeating. I can't say that documentation says exactly that, but it seems that it is working like that. Thus if timer is not periodic like in this case, Cancel has no effect.
UPDATE: this works in Windows 10 as it should.

Fail to Keep windows service with Timer alive

I have a windows service that I made for MY server...
I need to check every minute if there is some new info in my SQL Database.
So I made a windows service that create a Timer with interval of 1 minute.
But The windows service set the timer and ending the run.
It's goes like this:
Starting Service
Setting Timer with interval
Finishing and exiting Service <-- I want to keep it alive
As you can see the Service exit and I want the Windows service to run every minute without stopping....
I can see in the Event Viewer that there are the "Service started successfully." And the "Service stopped successfully."
What should I do?
P.S : I thought Timer should work with out exit... or may I wrong?
CODE:
Windows service:
static void Main(string[] args)
{
try
{
Utils.SetConfigFile();
var ServiceToRun = new TaoTimer();
ServiceToRun.Start();
}
catch (Exception ex)
{
EventLog.WriteEntry("Application", ex.ToString(), EventLogEntryType.Error);
}
}
TaoTimer:
public partial class TaoTimer : ServiceBase
{
List<TimerModel> timerList;
public TaoTimer()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
EventLog.WriteEntry("Started");
}
public void SetTimer(TimerModel timerModel)
{
int minute = 1000 * 60;
try
{
AlertTimer at = new AlertTimer(timerModel, minute);
at.Start();
}
catch
{
}
}
protected override void OnStop()
{
EventLog.WriteEntry("Stopped");
}
protected override void OnPause()
{
EventLog.WriteEntry("Paused");
}
protected override void OnContinue()
{
EventLog.WriteEntry("Continuing");
}
protected override void OnShutdown()
{
EventLog.WriteEntry("ShutDowned");
}
public void Start()
{
SetTimerList();
}
protected void SetTimerList()//Read Config from xml and start the timer
{
XElement root = XElement.Load(#"C:\TaoTimer\Data.xml");
timerList = new List<TimerModel>(from d in root.Descendants("Timer")
select new TimerModel(
d.Element("Id").Value.ToString(),
d.Element("Name").Value.ToString(),
d.Element("InterVal").Value.ToString(),
d.Element("TimeFormat").Value.ToString(),
d.Element("Day").Value.ToString(),
d.Element("TimeStamp").Value.ToString()));
timerList.ForEach(i => SetTimer(i));
}
}
AlertTimer:
public class AlertTimer
{
static System.Timers.Timer aTimer = new System.Timers.Timer();
public AlertTimer(TimerModel timerModel, int milliseconds)
{
aTimer.Elapsed += new ElapsedEventHandler((sender, e) => OnTimedEvent(sender, e, timerModel));
aTimer.Interval = milliseconds;
}
public void Start()
{
aTimer.Enabled = true;
}
public static void OnTimedEvent(object source, ElapsedEventArgs e, TimerModel timerModel)
{
getAbsenceContacts.Start();<-- NEVER GETS HERE....
}
}
You're not actually starting your service. You're calling a method named Start, which is not part of the Windows Service class hierarchy it's just a method you've defined. Your method runs and finishes, so the service exits.
Try this:
static void Main(string[] args)
{
try
{
Utils.SetConfigFile();
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new TaoTimer()
};
ServiceBase.Run(ServicesToRun);
}
catch (Exception ex)
{
EventLog.WriteEntry("Application", ex.ToString(), EventLogEntryType.Error);
}
}
public partial class TaoTimer : ServiceBase
{
...
protected override void OnStart(string[] args)
{
SetTimerList();
EventLog.WriteEntry("Started");
}
....
}
and remove the Start method from TaoTimer entirely.
You need to store your AlertTimer instances in something that will last the lifetime of the service (e.g. in a List<AlertTimer> declared as a field inside TaoTimer.
It's only really alluded to in the documentation for Timer that timer's, in and of themselves, don't prevent themselves from being garbage collected. The example says:
// Normally, the timer is declared at the class level,
// so that it stays in scope as long as it is needed.
// If the timer is declared in a long-running method,
// KeepAlive must be used to prevent the JIT compiler
// from allowing aggressive garbage collection to occur
// before the method ends. You can experiment with this
// by commenting out the class-level declaration and
// uncommenting the declaration below; then uncomment
// the GC.KeepAlive(aTimer) at the end of the method.
//System.Timers.Timer aTimer;
Now, whilst your timer's are declared at the class level inside of your AlertTimer class, there's nothing to stop the AlertTimer instances, themselves, from being collected. And the GC only keeps things alive that are transitively reachable. Once the AlertTimer instances are collectible, so are your Timer objects.

Preventing threads from processing further once call to stop is being made

I have a windows service which will start and stop the execution of some process that is being done with the held of Threads.
I have two classes as follows:
public class PerformTask
{
Thread _thread = null;
public void StartTask()
{
_thread = new Thread(new ThreadStart(DoSomeWork));
_thread.Start();
}
public void DoSomeWork()
{
// Do Some Work
_thread = null;
}
public void Abort()
{
if (_thread != null)
{
try
{
_thread.Abort();
}
catch (ThreadAbortException) {}
}
}
}
public class Engine
{
List<PerformTask> _task = new List<PerformTask>();
public void Start()
{
var task = new PerformTask();
_task.Add(task);
// Add task to the timed action queue
_actionQueue.Add(s => task.StartTask(), TimeSpan.FromSeconds(10));
}
public void Stop()
{
_task.ForEach(task => task.Abort());
_task.Clear();
_actionQueue.Stop();
_actionQueue.Clear();
}
}
The _actionQueue is a custom defined source code developed to perform a specified action at a recurring time interval specified. All the actions are kept in queue and invoked at the specified time interval.
Now, the Windows service's OnStart and OnStop method would call Engine class' Start and Stop method respectively.
What I want is when the windows service is stopped, all the threads that are running should stop their processing/execution.
But, what is happening here is as new thread instance is being created in I have a windows service which will start and stop the execution of some process that is being done with the held of Threads.
I have two classes as follows:
public class PerformTask
{
Thread _thread = null;
public void StartTask()
{
_thread = new Thread(new ThreadStart(DoSomeWork));
_thread.Start();
}
public void DoSomeWork()
{
// Do Some Work
_thread = null;
}
public void Abort()
{
if (_thread != null)
{
try
{
_thread.Abort();
}
catch (ThreadAbortException) {}
}
}
}
public class Engine
{
List<PerformTask> _task = new List<PerformTask>();
ActionQueue _actionQueue = new ActionQueue();
public void Start()
{
foreach(.....)
{
var task = new PerformTask();
_task.Add(task);
// Add task to the timed action queue
_actionQueue.Add(s => task.StartTask(), TimeSpan.FromSeconds(10));
}
_actionQueue.Start();
}
public void Stop()
{
_task.ForEach(task => task.Abort());
_task.Clear();
_actionQueue.Stop();
_actionQueue.Clear();
}
}
The ActionQueue is a custom defined source code developed to perform a specified action at a recurring time interval specified. All the actions are kept in queue and invoked at the specified time interval.
Now, the Windows service's OnStart and OnStop method would call Engine class' Start and Stop method respectively.
What I want is when the windows service is stopped, all the threads that are running should stop their processing/execution.
But, what is happening here is as new thread instance is being created in StartTask method, when I call the
_task.ForEach(task => task.Abort())
I do not have the correct instance of Thread, that is all the instance of
_thread = new Thread(....);
is not being accessed, as there would a multiple queues for the same PerformTask class.
Note: I cannot make changes to the ActionQueue.
Is Abort method a correct way of stopping the threads?
How can I stop all the threads (including all the instances of Thread class created by the source code)?
Usually you'd create a WaitHandle (a ManualResetEvent for example) like that:
ManualResetEvent stopAllThreads = new ManualResetEvent(false);
So the event is "not set". Change the loops in your thread method so that they loop until all work is done or the manual reset event is set.
while (!stopAllThreads.WaitOne(50))
or similar.
Then, in the service's OnStop method, you simply set the event (don't forget to reset it again in OnStart, otherwise the threads will not run again when the service is restarted):
stopAllThreads.Set();
and wait for all the threads to finish.
Actually, aborting threads is not a good way to stop threads - you should always go for something like the above.

Categories

Resources