假设我有一次性用品:
class MyDisposable : IDisposable
{
public void Dispose() { }
}
然后我希望每个线程都有自己的这个类的本地副本,每个线程一个:
private readonly ThreadLocal<MyDisposable> localMyDisposable
= new ThreadLocal<MyDisposable>(() => new MyDisposable());
现在我想确保在线程退出之前,我在值上调用Dispose()
,如果它已创建。有没有办法做到这一点?我的应用程序中的一些线程是短暂的,有些线程很长。我想确保任何短命的人都能处理他们的价值观。
答案 0 :(得分:5)
CLR中没有挂钩可以通知线程退出。线程本地值最终将被GC删除,但您似乎对快速处理感兴趣。
唯一的方法是让线程自己执行清理。
如果您使用任务而不是线程,则可以将延续附加到该任务,并确保在任务完成后清理并重置资源。但是,你不能在这里使用ThreadLocal
,因为永远不能保证在同一个线程上运行continuation。
答案 1 :(得分:1)
我最终实现了自己的名为ThreadLocalDisposable<T>
的类。它的作用是开始清理&#34;创建时的线程,并在ConcurrentDictionary
中跟踪每个线程创建的所有对象。一旦线程停止,它最终会调用它上面的Action<T> cleanupMethod
,如果你愿意的话,它可以只是x => x.Dispose()
。
请注意清理代码是在清理线程上调用的,而不是在它实际创建和使用的线程上调用,所以不管你做什么都必须是线程安全的。
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
}
}
}
已知问题:我的意思是让这个课程在应用程序的生命周期中存活,所以当你处理它时,它只是尽力清理,但它不确定清理它整个对象列表,因为其他线程仍然可以调用Value
getter并在处理线程获得其最后一个可枚举副本后创建新对象。