Reputation: 1155
Here is a simple example:
class MyClass : IDisposable
{
bool working;
public MyClass()
{
working = true;
Thread t = new Thread(Worker);
t.Start();
}
void Worker()
{
while(working)
{}
}
public void Dispose()
{
working = false;
}
}
Is it okay to do this ? Will Dispose() even be called while the thread is still running? If not, how to better do it ?
Upvotes: 1
Views: 492
Reputation: 3171
You shouldn't rely on that your Dispose
method will be called.
You can wrap you class instance with WeakReference
. The thread should stop its work once the reference is garbage collected.
class MyClass : IDisposable
{
class CancellationSignal
{
WeakReference _reference;
public bool IsWorking { get { return _working && _reference.IsAlive; } }
volatile bool _working;
public CancellationSignal(WeakReference reference)
{
if (reference == null) throw new ArgumentNullException("reference");
_reference = reference;
}
public void Signal()
{
_working = false;
}
}
CancellationSignal _cancellationSignal;
public MyClass()
{
_cancellationSignal = new CancellationSignal(new WeakReference(this));
Thread t = new Thread(Worker);
t.Start(_cancellationSignal);
}
static void Worker(object state)
{
var s = (CancellationSignal)state;
while (s.IsWorking)
{
//...
}
}
public void Dispose()
{
_cancellationSignal.Signal();
}
}
Personally I prefer to avoid using finalizers but I still have to say that another solution would be calling Signal
in finalizer (and then you can remove WeakReference
stuff):
~MyClass()
{
_cancellationSignal.Signal();
}
Notice that _working
is declared as volatile. You can also use ManualResetEventSlim
or even CancellationToken
instead.
In both approaches (WeakReference
and finalizer) you have to avoid passing this
reference to Thread (because it would prevent your class instance from being garbage collected) therefore I added a CancellationSignal
class and marked Worker
as static.
Upvotes: 1
Reputation: 223187
Dispose
will not be called unless you call it, either explicitly or through enclosing MyClass
instantiation in using
statement.
Will Dispose() even be called while the thread is still running?
It can be called like:
using (MyClass m = new MyClass())
{
System.Threading.Thread.Sleep(2000);
}
So once the using
block ends, it will call the Dispose
method in your class.
(Although your variable should be volatile
to indicate that the field might be modified by multiple threads)
But the important thing to note is, that there is no magical thing about Dispose
, even the using
statement translates into try-finally
, where Dispose
is explicitly called in the finally
block.
Upvotes: 1