Error while unloading appdomain. (Exception from HRESULT: 0x80131015), inside Windows Service - c#

I receive this error in a windows service.
This is the same service that I've previously discussed in my question here
The code is revised to use Parallel.ForEach (my own version as this is a 3.5 windows service). The reason for the Parallel use is down to the fact that it simply took too long to Unload each domain and running them in parallel should prove to be faster (appears to be even though there is only one thread that's doing each Unload?!).
Based on other posts, I can only guess that this is somehow down to the fact I am using a ThreadPool Thread to Unload the AppDomains. I just can't see how to avoid it?
public partial class SomeService : ServiceBase
{
private Manager _appDomainManager;
protected override void OnStop()
{
_appDomainManager.Dispose();
}
}
public class Manager : IDisposable
{
public void Dispose()
{
Log.Debug("Disposing");
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
// dispose managed resources
Parallel.For(0, appdomains.Length, UnloadAppDomian);
}
_disposed = true;
}
}
private UnloadAppDomain(int appDomainIndex);
public static class Parallel35
{
public static void For(int start, int end, Action<int> action)
{
var waitHandles = new WaitHandle[end - start];
for (int j = 0; j < waitHandles.Length; j++)
{
waitHandles[j] = new ManualResetEvent(false);
}
for (int i = start; i < end; i++)
{
int i1 = i - start;
ThreadPool.QueueUserWorkItem(
state =>
{
try
{
action((int) state);
}
finally
{
((ManualResetEvent) waitHandles[i1]).Set();
}
}, i);
}
WaitHandle.WaitAll(waitHandles);
}
}

I tracked down this as a bug to one of the AppDomains on exit waiting for a WaitHandle that's never set.
If a thread does not abort, for example because it is executing
unmanaged code, or because it is executing a finally block, then after
a period of time a CannotUnloadAppDomainException is thrown in the
thread that originally called Unload.
The AppDomain now unloads relatively quickly and my service stops quite quickly.

Try to unload all AppDomains in a single background task instead of one background task for each AppDomain and use ServiceBase.RequestAdditionalTime so that the SCM does not mark your service as not responsive.

Related

Why is my System.Threading.Task.ContinueWith firing at the wrong time

I am trying to handle exceptions from a System.Threading.Tasks.Task
I haven't used these before, and seem to be misunderstanding how the ContinueWith works; thus my ContinueWith is firing at the wrong time.
Given the following; workers is just a list of my long running processes.
......
workers.Add(new Workers.Tests.TestWorker1());
workers.Add(new Workers.Tests.TestWorker2());
// Start all the workers.
workers.ForEach(worker =>
{
// worker.Start schedules a timer and calls DoWork in the worker
System.Threading.Tasks.Task task = new System.Threading.Tasks.Task(worker.Start);
task.ContinueWith(ExceptionHandler, TaskContinuationOptions.OnlyOnFaulted);
task.Start();
})
.....
My handler method is
private void ExceptionHandler(System.Threading.Tasks.Task arg1, object arg2)
{
DebugLogger.Write("uh oh.. it died");
}
My TestWorker's are:
class TestWorker1 : Worker
{
int count = 1;
public override void DoWork(object timerState)
{
DebugLogger.Write(string.Format("{0} ran {1} times", workerName, count));
count++;
ScheduleTimer();
}
}
And
class TestWorker2 : Worker
{
int count = 1;
public override void DoWork(object timerState)
{
DebugLogger.Write(string.Format("{0} ran {1} times", workerName, count));
count++;
if (count == 3)
throw new Exception("I'm going to die....");
ScheduleTimer();
}
}
ScheduleTimer() simply sets an interval for DoWork to be run
What happens...
When I debug, all tasks are created and started. As soon as theDoWork has called ScheduleTimer() for the first time, my ExceptionHandler is hit; as shown in this screenshot - this happens for both workers.
When the exception is hit in TestWorker2 the debugger will not move on from there - in that i press continue, hoping to hit my ExceptionHandler, and the debugger just keeps throwing the exception.
What I am hoping to achieve
I would like my ExceptionHandler to only fire when an exception within the running tasks is thrown. I'm finding the only time i get into my ExceptionHandler is when it's run, and my actual exception just keeps looping.
What am i missing?
Per comment, here is the code for the main Worker
public abstract class Worker : IDisposable
{
internal string workerName;
internal Timer scheduler;
internal DateTime scheduledTime;
public Worker()
{
string t = this.GetType().ToString();
workerName = t.Substring(t.LastIndexOf(".") + 1).AddSpacesBeforeUppercase(true).Trim();
}
/// <summary>
/// Set to true when the worker is performing its task, false when its complete
/// </summary>
public bool IsCurrentlyProcessing { get; set; }
public void Start()
{
DebugLogger.Write(workerName + " Started");
ScheduleTimer();
}
/// <summary>
/// default functionality for setting up the timer.
/// Typically, the timer will fire in 60 second intervals
/// Override this method in child classes for different functionality
/// </summary>
public virtual void ScheduleTimer()
{
scheduler = new Timer(new TimerCallback(DoWork));
int interval = 60;
int.TryParse(ConfigurationManager.AppSettings[string.Format("{0}{1}", workerName.Replace(" ", ""), "Interval")], out interval);
scheduledTime = DateTime.Now.AddSeconds(interval);
if (DateTime.Now > scheduledTime)
scheduledTime = scheduledTime.AddSeconds(interval);
int dueTime = Convert.ToInt32(scheduledTime.Subtract(DateTime.Now).TotalMilliseconds);
scheduler.Change(dueTime, Timeout.Infinite);
}
public abstract void DoWork(object timerState);
public void Stop()
{
// kill stuff
if (scheduler != null)
scheduler.Dispose();
DebugLogger.Write(workerName + " stop");
this.Dispose();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
if (disposing)
{
// any specific cleanup
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
From your screenshot it appears that arg2 is your TaskContinuationOptions.OnlyOnFaulted object, that is the biggest clue of what is going wrong. Because you passed in a Action<Task, Object> it is using the Task.ContinueWith Method (Action<Task, Object>, Object) overload of ContinueWith, this is causing your continuation options to be passed in as the state parameter.
Either change ExceptionHandler to
private void ExceptionHandler(System.Threading.Tasks.Task arg1)
{
DebugLogger.Write("uh oh.. it died");
}
so you will use the Task.ContinueWith(Action<Task>, TaskContinuationOptions) overload, or you can change your call to
task.ContinueWith(ExceptionHandler, null, TaskContinuationOptions.OnlyOnFaulted);
so that you will start using the Task.ContinueWith(Action<Task, Object>, Object, TaskContinuationOptions) overload.
Might be caused by your logging component not supporting multiple concurrent writes.
If it's possible for you, I'd suggest you refactor the code to the async/await pattern, it will be much more readable.
Let's say you create a list of all the tasks you want to run:
List<Task> tasks = new List<Task>();
workers.ForEach(worker => tasks.Add(Task.Run(() => worker.Start())));
and then use await on the list surrounded by a try catch block:
try
{
await Task.WhenAll(tasks);
}
catch (Exception ex)
{
DebugLogger.Write("uh oh.. it died");
}
Also, make sure you are not doing any Thread.Wait(xxx) calls (or any other Thread.XXX calls for that matter) inside ScheduleTimer(), because tasks and threads don't play nice together.
Hope it helps!

Thread Safe decrement counter

Problem
In a project case I need to create multiple threads that are picking tasks from a queue and running them. Some of these tasks cannot run if a group of other tasks are still running. Consider something like file copy and defrag (runs when system is idle) in windows .
Solution
To implement this, I created a class based on
System.Threading.CountdownEvent.
Whenever a thread picks a blocking task from queue, they will Increment the CounterEvent and after they finished their job they will Decrement the CounterEvent.
If a thread picks a low priority task, it will Wait until CounterEvent is zero then starts running.
A low priority taks can immediately start with Reset of CounterEvent
Main thread or a parallel thread can monitor the status of lock by querying the CurrentCount.
Here is the Code:
using System;
using System.Diagnostics.Contracts;
using System.Threading;
public class CounterEvent : IDisposable {
private volatile int m_currentCount;
private volatile bool m_disposed;
private ManualResetEventSlim m_event;
// Gets the number of remaining signals required to set the event.
public int CurrentCount {
get {
return m_currentCount;
}
}
// Allocate a thin event, Create a latch in signaled state.
public CounterEvent() {
m_currentCount = 0;
m_event = new ManualResetEventSlim();
m_event.Set(); //
}
// Decrements the counter. if counter is zero signals other threads to continue
public void Decrement() {
ThrowIfDisposed();
Contract.Assert(m_event != null);
int newCount = 0;
if (m_currentCount >= 0) {
#pragma warning disable 0420
newCount = Interlocked.Decrement(ref m_currentCount);
#pragma warning restore 0420
}
if (newCount == 0) {
m_event.Set();
}
}
// increments the current count by one.
public void Increment() {
ThrowIfDisposed();
#pragma warning disable 0420
Interlocked.Increment(ref m_currentCount);
#pragma warning restore 0420
}
// Resets the CurrentCount to the value of InitialCount.
public void Reset() {
ThrowIfDisposed();
m_currentCount = 0;
m_event.Set();
}
// Blocks the current thread until the System.Threading.CounterEvent is set.
public void Wait() {
ThrowIfDisposed();
m_event.Wait();
}
/// <summary>
/// Throws an exception if the latch has been disposed.
/// </summary>
private void ThrowIfDisposed() {
if (m_disposed) {
throw new ObjectDisposedException("CounterEvent");
}
}
// According to MSDN this is not thread safe
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
// According to MSDN Dispose() is not thread-safe.
protected virtual void Dispose(bool disposing) {
if (disposing) {
m_event.Dispose();
m_disposed = true;
}
}
}
Question
will this code work as expected?
Any flaws that I didn't see in it?
Is there any better option doing this?
Note
Application is written with System.Threading.Thread and cost of converting it for me is very high, however a great replacement solution always worth working on for future.
This should be one atomic operation and it is not threadsafe if you do it like this
if (m_currentCount >= 0)
{
newCount = Interlocked.Decrement(ref m_currentCount);
}
It may happen that m_currentCount is changed between the if and the Interlocked.Decrement
You should rewrite your logic to use Interlocked.CompareExchange I would also use Interlocked.Exchange in every place where you assign to m_currentCount then you don´t need volatile and the pragma You should also be aware of that under very heavy load it can happen that a reset event Set is getting lost

Is this an appropriate place to invoke Thread.Abort()?

I have some code that I borrowed from Steve Marx. The main block is used in an azure worker role thread to take out a lease on an azure blob. This provides a locking mechanism for synchronizing across multiple worker instances when you only want one instance to process a job at a time. However since you might have jobs that will take longer to complete than a blob lease timeout, a new thread is spawned to renew the blob lease every so often.
This renewal thread sleeps and renews on an infinite loop. When the main thread exits (via Dispose in the class' consumer), renewalThread.Abort() is invoked. This causes all kinds of ThreadAbortExceptions to be thrown in the worker role.
I'm wondering, is this a better way to handle this? What I don't like about it is that you can have several renewal threads that remain asleep after the consumer that spawned them has been disposed. Is there anything bad about the code below? If so, is there a better way? Or is Thread.Abort() appropriate here?
public class AutoRenewLease : IDisposable
{
private readonly CloudBlockBlob _blob;
public readonly string LeaseId;
private Thread _renewalThread;
private volatile bool _isRenewing = true;
private bool _disposed;
public bool HasLease { get { return LeaseId != null; } }
public AutoRenewLease(CloudBlockBlob blob)
{
_blob = blob;
// acquire lease
LeaseId = blob.TryAcquireLease(TimeSpan.FromSeconds(60));
if (!HasLease) return;
// keep renewing lease
_renewalThread = new Thread(() =>
{
try
{
while (_isRenewing)
{
Thread.Sleep(TimeSpan.FromSeconds(40.0));
if (_isRenewing)
blob.RenewLease(AccessCondition
.GenerateLeaseCondition(LeaseId));
}
}
catch { }
});
_renewalThread.Start();
}
~AutoRenewLease()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing && _renewalThread != null)
{
//_renewalThread.Abort();
_isRenewing = false;
_blob.ReleaseLease(AccessCondition
.GenerateLeaseCondition(LeaseId));
_renewalThread = null;
}
_disposed = true;
}
}
Update
Let's say you have an azure worker role deployed with 2 or more instances. Let's also say you have a job that both instances have the ability to process for you. During the worker roles Run method you might have something like this:
public override void Run()
{
while (true)
{
foreach (var task in _workforce)
{
var job = task.Key;
var workers = task.Value;
foreach (var worker in workers)
worker.Perform((dynamic)job);
}
Thread.Sleep(1000);
}
}
Every second, the role will check to see if certain jobs are scheduled to run, and if they are, process them. However to avoid having both role instances process the same job, you first take out a lease on a blob. By doing that, the other instance cannot access the blob, so it is effectively blocked until the first instance is finished processing. (Note: taking out a new lease happens within the .Perform method above.)
Now, let's say a job can take anywhere from 1 to 100 seconds to complete. There is a built-in timeout on blob leases, so if you want to keep the other role blocked until the process is finished, you have to periodically renew that lease, to keep it form timing out. That is what the above class encapsulates -- automatically renewing a lease until you dispose of it as a consumer.
My question is mainly about the sleep timeout in the renewalThread. Say the job completed in 2 seconds. The renewalThread will gracefully exit (I think) but not for another 38 seconds. That is where the meat of uncertainty in my question lies. The original code invoked renewalThread.Abort(), which caused it to cease immediately. Is it better to do that, or let it sleep and exit gracefully at a later time? If you are heartbeating the role's Run method at once per second, you could have up to 40 of these renewal threads waiting to exit gracefully. If you have different jobs blocking on different blobs, that number gets multiplied by the number of blobs being leased. However if you do it with Thread.Abort(), you get just as many ThreadAbortExceptions sizzling on the stack.
As I understand it, you have a job that requires a lease on some object. That lease can expire, so you want something to continually renew the lease as long as the job is running.
You don't need a thread in a sleep loop. You need a timer. For example:
public class AutoRenewLease : IDisposable
{
private readonly CloudBlockBlob _blob;
public readonly string LeaseId;
private System.Threading.Timer _renewalTimer;
private volatile bool _isRenewing = true;
private bool _disposed;
public bool HasLease { get { return LeaseId != null; } }
public AutoRenewLease(CloudBlockBlob blob)
{
_blob = blob;
// acquire lease
LeaseId = blob.TryAcquireLease(TimeSpan.FromSeconds(60));
if (!HasLease) return;
_renewalTimer = new System.Threading.Timer(x =>
{
if (_isRenewing)
{
blob.RenewLease(AccessCondition
.GenerateLeaseCondition(LeaseId));
}
}, null, TimeSpan.FromSeconds(40), TimeSpan.FromSeconds(40));
~AutoRenewLease()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing && _renewalTimer != null)
{
_isRenewing = false;
_renewalTimer.Dispose();
_blob.ReleaseLease(AccessCondition
.GenerateLeaseCondition(LeaseId));
_renewalTimer = null;
}
_disposed = true;
}
}
There's no need to waste the resources that a thread uses just so that it can sleep most of the time. Using a timer eliminates polling and also eliminates the need for Thread.Abort.
Abort should be avoided whenever possible. There are some places you really need it, but for this scenario I think we can do it better without abort.
Make it simple with ManualResetEvent, This will stop your thread gracefully and immediately without the use of Abort.
private ManualResetEvent jobSignal = new ManualResetEvent(false);
public AutoRenewLease(CloudBlockBlob blob)
{
_blob = blob;
// acquire lease
LeaseId = blob.TryAcquireLease(TimeSpan.FromSeconds(60));
if (!HasLease) return;
// keep renewing lease
_renewalThread = new Thread(() =>
{
try
{
while (_isRenewing)
{
if(jobSignal.WaitOne(TimeSpan.FromSeconds(40.0)))
{
//Disposed so stop working
jobSignal.Dispose();
jobSignal = null;
return;
}
if (_isRenewing)
blob.RenewLease(AccessCondition
.GenerateLeaseCondition(LeaseId));
}
}
catch (Exception ex) {//atleast log it }
});
_renewalThread.Start();
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing && _renewalThread != null)
{
jobSignal.Set();//Signal the thread to stop working
_isRenewing = false;
_blob.ReleaseLease(AccessCondition
.GenerateLeaseCondition(LeaseId));
_renewalThread = null;
}
_disposed = true;
}
Hope this helps.

Multiple calls (at once) to a method that can only run one at a time

I have a business logic method that has to be finished before it can be called again. Multiple clients have the ability to call it at once:
public void DoSomething() {}
I was thinking about solving it by making the method private, and creating a new public method to put the requests on a queue:
public void QueueSomeWork()
{
// put on a Queue object
// How will DoSomething get invoked now?
}
private void DoSomething() {}
I'm trying to solve this issue in an elegant way. My issue is how DoSomething() will know to run. I thought about creating a timer to check the queue, but then it would be running 24/7 for something that happens maybe twice per year.
Another thought is to have DoSomething() fire an event when it's done that something else would subscribe to, pick some work off the queue, and call DoSomething(). Is there a better way?
Why don't you use a lock guard?
Eg :
private static Object lockGuard = new Object();
public void DoSomething()
{
lock (lockGuard)
{
//logic gere
}
}
Locking a resource will prevent access from multiple threads in the same time.
More on lock : http://msdn.microsoft.com/en-us/library/c5kehkcz(v=vs.110).aspx
If the numbers are not so high (it depends how DoSomething internals consume resource); I would go with this:
public static async void QueueSomeWork()
{
await Task.Run(() => { DoSomething(); });
}
static readonly object lockObject = new object();
static void DoSomething()
{
lock (lockObject)
{
// implementation
}
}
And if the numbers are higher, you should put a limit on number of allowed queued tasks:
static long numberOfQueuedTasks = 0;
const long MAX_TASKS = 10000; // it depends how DoSomething internals consume resource
public static async void QueueSomeWork()
{
if (numberOfQueuedTasks > MAX_TASKS)
{
var wait = new SpinWait();
while (numberOfQueuedTasks > MAX_TASKS) wait.SpinOnce();
}
await Task.Run(() => { Interlocked.Increment(ref numberOfQueuedTasks); DoSomething(); });
}
static readonly object lockObject = new object();
static void DoSomething()
{
try
{
lock (lockObject)
{
// implementation
}
}
finally
{
Interlocked.Decrement(ref numberOfQueuedTasks);
}
}
Simple way of doing it is by decorating the method with MethodImplOptions.Synchronized, whose function is similar to the synchronized keyword in Java:
[MethodImpl(MethodImplOptions.Synchronized)]
private void DoSomething()
{
// ...
}
The main downside is that this will lock on the current instance, which might lead to deadlock if you're already using locking elsewhere.
Here is an idea. You'd probably want to lock the doSomethingCount when using it, but as for queuing the DoSomething and going on this might work because it runs on a separate thread. Since you were ok with a queue, I assume you want fire and forget and don't actually need to block the caller.
// This will increment the count and kick off the process of going through
// the calls if it isn't already running. When it is done, it nulls out the task again
// to be recreated when something is queued again.
public static void QueueSomething()
{
doSomethingCount++;
if (doSomethingTask == null)
{
doSomethingTask =
Task.Run((Action)(() =>
{
while (doSomethingCount > 0)
{
DoSomething();
doSomethingCount--;
}
}))
.ContinueWith(t => doSomethingTask = null);
}
}
// I just put something in here that would take time and have a measurable result.
private static void DoSomething()
{
Thread.Sleep(50);
thingsDone++;
}
// These two guys are the data members needed.
private static int doSomethingCount = 0;
private static Task doSomethingTask;
// This code is just to prove that it works the way I expected. You can use it too.
public static void Run()
{
for (int i = 0; i < 10; i++)
{
QueueSomething();
}
while (thingsDone < 10)
{
Thread.Sleep(100);
}
thingsDone = 0;
QueueSomething();
while (thingsDone < 1)
{
Thread.Sleep(100);
}
Console.WriteLine("Done");
}
// This data point is just so I could test it. Leaving it in so you can prove it yourself.
private static int thingsDone = 0;
if this is code-only issue, the lock solution is good. But sometimes you run a DB transaction, where series of objects (records) have to be modified with no interference. Good example is when you re-run sequence enumeration of DB records. You can create a lock table in DB and lock a specific defined record in it for update first thing in the transaction. This will prevent other transactions created by your application (in the same code area) to even get to the table you updating. And second call will only proceed after the first one is done. Just a tip.

C# Threading Patterns - is this a good idea?

I was playing with a project of mine today and found an interesting little snippet, given the following pattern, you can safely cleanup a thread, even if it's forced to close early. My project is a network server where it spawns a new thread for each client. I've found this useful for early termination from the remote side, but also from the local side (I can just call .Abort() from inside my processing code).
Are there any problems you can see with this, or any suggestions you'd make to anyone looking at a similar approach?
Test case follows:
using System;
using System.Threading;
class Program
{
static Thread t1 = new Thread(thread1);
static Thread t2 = new Thread(thread2);
public static void Main(string[] args)
{
t1.Start();
t2.Start();
t1.Join();
}
public static void thread1() {
try {
// Do our work here, for this test just look busy.
while(true) {
Thread.Sleep(100);
}
} finally {
Console.WriteLine("We're exiting thread1 cleanly.\n");
// Do any cleanup that might be needed here.
}
}
public static void thread2() {
Thread.Sleep(500);
t1.Abort();
}
}
For reference, without the try/finally block, the thread just dies as one would expect.
Aborting another thread at all is just a bad idea unless the whole application is coming down. It's too easy to leave your program in an unknown state. Aborting your own thread is occasionally useful - ASP.NET throws a ThreadAbortException if you want to prematurely end the response, for example - but it's not a terribly nice design.
Safe clean-up of a thread should be mutual - there should be some shared flag requesting that the thread shuts down. The thread should check that flag periodically and quit appropriately.
Whether or not this will "safely" cleanup a thread cannot be discerned from a general code sample unfortunately. It's highly dependent upon the actual code that is executed within the thread. There are multiple issues you must consider. Each represents a potential bug in the code.
If the thread is currently in native code, it will not immediately respect the Thread.Abort call. It will do all of the work it wants to do in native code and will not throw until the code returns back to managed. Until this happens thread2 will hang.
Any native resources that are not freed in a finally block will be leaked in this scenario. All native resources should be freed in a finally block but not all code does this and it's an issue to consider.
Any locks that are not freed in a finally block will remain in a lock'd state and can lead to future dead locks.
There are other issues which are slipping my mind at the moment. But hopefully this will give you some guidance with your application.
It is generally not a good idea to abort threads. What you can do is poll for a stopRequested flag which can be set from other threads. Below is a sample WorkerThread class for your reference. For more information on how to use it, please refer to http://devpinoy.org/blogs/jakelite/archive/2008/12/20/threading-patterns-the-worker-thread-pattern.aspx
public abstract class WorkerThreadBase : IDisposable
{
private Thread _workerThread;
protected internal ManualResetEvent _stopping;
protected internal ManualResetEvent _stopped;
private bool _disposed;
private bool _disposing;
private string _name;
protected WorkerThreadBase()
: this(null, ThreadPriority.Normal)
{
}
protected WorkerThreadBase(string name)
: this(name, ThreadPriority.Normal)
{
}
protected WorkerThreadBase(string name,
ThreadPriority priority)
: this(name, priority, false)
{
}
protected WorkerThreadBase(string name,
ThreadPriority priority,
bool isBackground)
{
_disposing = false;
_disposed = false;
_stopping = new ManualResetEvent(false);
_stopped = new ManualResetEvent(false);
_name = name == null ? GetType().Name : name; ;
_workerThread = new Thread(threadProc);
_workerThread.Name = _name;
_workerThread.Priority = priority;
_workerThread.IsBackground = isBackground;
}
protected bool StopRequested
{
get { return _stopping.WaitOne(1, true); }
}
protected bool Disposing
{
get { return _disposing; }
}
protected bool Disposed
{
get { return _disposed; }
}
public string Name
{
get { return _name; }
}
public void Start()
{
ThrowIfDisposedOrDisposing();
_workerThread.Start();
}
public void Stop()
{
ThrowIfDisposedOrDisposing();
_stopping.Set();
_stopped.WaitOne();
}
public void WaitForExit()
{
ThrowIfDisposedOrDisposing();
_stopped.WaitOne();
}
#region IDisposable Members
public void Dispose()
{
dispose(true);
}
#endregion
public static void WaitAll(params WorkerThreadBase[] threads)
{
WaitHandle.WaitAll(
Array.ConvertAll<WorkerThreadBase, WaitHandle>(
threads,
delegate(WorkerThreadBase workerThread)
{ return workerThread._stopped; }));
}
public static void WaitAny(params WorkerThreadBase[] threads)
{
WaitHandle.WaitAny(
Array.ConvertAll<WorkerThreadBase, WaitHandle>(
threads,
delegate(WorkerThreadBase workerThread)
{ return workerThread._stopped; }));
}
protected virtual void Dispose(bool disposing)
{
//stop the thread;
Stop();
//make sure the thread joins the main thread
_workerThread.Join(1000);
//dispose of the waithandles
DisposeWaitHandle(_stopping);
DisposeWaitHandle(_stopped);
}
protected void ThrowIfDisposedOrDisposing()
{
if (_disposing)
{
throw new InvalidOperationException(
Properties.Resources.ERROR_OBJECT_DISPOSING);
}
if (_disposed)
{
throw new ObjectDisposedException(
GetType().Name,
Properties.Resources.ERROR_OBJECT_DISPOSED);
}
}
protected void DisposeWaitHandle(WaitHandle waitHandle)
{
if (waitHandle != null)
{
waitHandle.Close();
waitHandle = null;
}
}
protected abstract void Work();
private void dispose(bool disposing)
{
//do nothing if disposed more than once
if (_disposed)
{
return;
}
if (disposing)
{
_disposing = disposing;
Dispose(disposing);
_disposing = false;
//mark as disposed
_disposed = true;
}
}
private void threadProc()
{
Work();
_stopped.Set();
}
}

Categories