Let's say I have a disposable type:
class MyDisposable : IDisposable
{
public void Dispose() { }
}
Then I want every thread to have its own local copy of this class, one per thread:
private readonly ThreadLocal<MyDisposable> localMyDisposable
= new ThreadLocal<MyDisposable>(() => new MyDisposable());
Now I want to make sure that right before the thread exits, I call Dispose()
on the value, if it was created. Is there any way to do that? Some threads in my application are short-lived and some are long-lived. I want to make sure any short-lived ones dispose of their values.
Abort(Object) Generally, this method is used to terminate the thread. Syntax: public void Abort(object information); Here, the information contains any information that you want to pass in a thread when it is being stopped.
ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID). For example, the class below generates unique identifiers local to each thread. A thread's id is assigned the first time it invokes ThreadId.
There is no hook in the CLR to be notified about a thread exit. The thread-local value will be deleted eventually by the GC but you seem to be interested in quick disposal.
The only way is to make the thread perform the cleanup itself.
If you use tasks instead of threads you can attach a continuation to that task and make sure the resource is cleaned up and reset after the task completes. You can't use a ThreadLocal
here, though, because continuations are never guaranteed to run on the same thread.
I ended up implementing my own class called ThreadLocalDisposable<T>
. What it does is start a "cleanup" thread when created, and keep track of all the objects it has created, per thread, in a ConcurrentDictionary
. Once a thread has stopped, it will eventually call the Action<T> cleanupMethod
on it, which can simply be x => x.Dispose()
if you like.
Just beware that the cleanup code is called on the cleanup thread, not on the thread that it was actually created and used on, so whatever you do there has to be thread-safe.
public class ThreadLocalDisposable<T> : IDisposable
where T : IDisposable
{
private readonly ConcurrentDictionary<Thread, T> valuesByThread
= new ConcurrentDictionary<Thread, T>();
private readonly Func<T> factoryMethod;
private object disposedLock = new object();
private bool disposed = false;
public ThreadLocalDisposable(
Func<T> factoryMethod,
Action<T> cleanupMethod)
{
if (factoryMethod == null) throw new ArgumentNullException("factoryMethod");
if (cleanupMethod == null) throw new ArgumentNullException("cleanupMethod");
this.factoryMethod = factoryMethod;
var disposerThread = new Thread(() =>
{
bool exit = false;
while (!exit)
{
Thread.Sleep(1000);
var removedValuesByThread = new List<KeyValuePair<Thread, T>>();
foreach (var valueByThread in this.valuesByThread)
{
if ((valueByThread.Key.ThreadState & ThreadState.Stopped) == ThreadState.Stopped)
{
removedValuesByThread.Add(valueByThread);
}
}
foreach (var removedValueByThread in removedValuesByThread)
{
T value;
while (!this.valuesByThread.TryRemove(removedValueByThread.Key, out value))
{
Thread.Sleep(2);
}
cleanupMethod(value);
}
lock (this.disposedLock)
{
exit = this.disposed;
}
}
foreach (var valueByThread in this.valuesByThread)
{
var value = valueByThread.Value;
cleanupMethod(value);
}
this.valuesByThread.Clear();
});
disposerThread.Start();
}
public T Value
{
get
{
if (!this.valuesByThread.ContainsKey(Thread.CurrentThread))
{
var newValue = this.factoryMethod();
while (!this.valuesByThread.TryAdd(Thread.CurrentThread, newValue))
{
Thread.Sleep(2);
}
}
var result = this.valuesByThread[Thread.CurrentThread];
return result;
}
}
public void Dispose()
{
lock (this.disposedLock)
{
this.disposed = true; // tells disposer thread to shut down
}
}
}
Known issues: I meant for this class to live for the lifetime of the application, so when you dispose of it, it just does its best to clean-up, but it's not certain to clean-up the whole list of objects, because other threads could still call the Value
getter and create new objects after the disposing thread got its last enumerable copy.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With