线程退出时如何处理ThreadLocal值?

时间:2014-06-26 14:08:29

标签: c# .net multithreading idisposable thread-local-storage

假设我有一次性用品:

class MyDisposable : IDisposable
{
    public void Dispose() { }
}

然后我希望每个线程都有自己的这个类的本地副本,每个线程一个:

private readonly ThreadLocal<MyDisposable> localMyDisposable
    = new ThreadLocal<MyDisposable>(() => new MyDisposable());

现在我想确保在线程退出之前,我在值上调用Dispose(),如果它已创建。有没有办法做到这一点?我的应用程序中的一些线程是短暂的,有些线程很长。我想确保任何短命的人都能处理他们的价值观。

2 个答案:

答案 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并在处理线程获得其最后一个可枚举副本后创建新对象。