28

を使用してIDisposableを実装する場合、ThreadLocal内に保持されているメンバーをどのように破棄することになっていますかThreadLocal<T>T

ILSpyによると、ThreadLocalのDispose()メソッドとDispose(bool)メソッドは次のとおりです。

public void Dispose()
{
    this.Dispose(true);
    GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
    int currentInstanceIndex = this.m_currentInstanceIndex;
    if (currentInstanceIndex > -1 && Interlocked.CompareExchange(ref this.m_currentInstanceIndex, -1, currentInstanceIndex) == currentInstanceIndex)
    {
        ThreadLocal<T>.s_availableIndices.Push(currentInstanceIndex);
    }
    this.m_holder = null;
}

ThreadLocalが子メンバーに対してDisposeを呼び出そうとしているようには見えません。内部で割り当てられた各スレッドを参照する方法がわからないので、処理できます。


次のコードでテストを実行しましたが、クラスが破棄されることはありません

static class Sandbox
{
    static void Main()
    {
        ThreadLocal<TestClass> test = new ThreadLocal<TestClass>();
        test.Value = new TestClass();

        test.Dispose();
        Console.Read();
    }
}

class TestClass : IDisposable
{
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    protected void Dispose(bool Disposing)
    {
        Console.Write("I was disposed!");
    }
}
4

6 に答える 6

14

のコードをThreadLocal<T>調べて、現在の動作を確認しましDisposeたが、ブードゥー教のようです。明らかにスレッド関連のものを処分します。

Tただし、それ自体が破棄可能な場合、値は破棄されません。

さて、私は解決策を持っています-ThreadLocalDisposables<T>クラスですが、完全な定義を与える前に、このコードを書いた場合に何が起こるかを考える価値があります:

var tl = new ThreadLocalDisposables<IExpensiveDisposableResource>();
tl.Value = myEdr1;
tl.Value = myEdr2;
tl.Dispose();

両方myEdr1myEdr2両方を処分する必要がありますか?それともただmyEdr2?または、配属myEdr1されたときに処分する必要がありますか?myEdr2

セマンティクスがどうあるべきかは私には明らかではありません。

ただし、このコードを書いた場合、次のことは明らかです。

var tl = new ThreadLocalDisposables<IExpensiveDisposableResource>(
    () => new ExpensiveDisposableResource());
tl.Value.DoSomething();
tl.Dispose();

次に、各スレッドのファクトリによって作成されたリソースを破棄する必要があると思います。

したがって、破棄可能な値の直接割り当てを許可せThreadLocalDisposablesず、ファクトリー コンストラクターのみを許可します。

ここにありThreadLocalDisposablesます:

public class ThreadLocalDisposables<T> : IDisposable
    where T : IDisposable
{
    private ThreadLocal<T> _threadLocal = null;
    private ConcurrentBag<T> _values = new ConcurrentBag<T>();

    public ThreadLocalDisposables(Func<T> valueFactory)
    {
        _threadLocal = new ThreadLocal<T>(() =>
        {
            var value = valueFactory();
            _values.Add(value);
            return value;
        });
    }

    public void Dispose()
    {
        _threadLocal.Dispose();
        Array.ForEach(_values.ToArray(), t => t.Dispose());
    }

    public override string ToString()
    {
        return _threadLocal.ToString();
    }

    public bool IsValueCreated
    {
        get { return _threadLocal.IsValueCreated; }
    }

    public T Value
    {
        get { return _threadLocal.Value; }
    }
}

これは役に立ちますか?

于 2011-10-06T06:05:03.953 に答える
2

通常、アンマネージ リソースを保持するクラスを明示的に破棄しない場合、最終的にガベージ コレクターが実行されて破棄されます。これを行うには、リソースを破棄するファイナライザーがクラスに必要です。サンプル クラスにはファイナライザーがありません。

ThreadLocal<T>ここで、 T がa 内に保持されているクラスを破棄するには、IDisposable自分で行う必要もあります。ThreadLocal<T>は単なるラッパーであり、ラップされた参照自体が破棄されたときに、ラップされた参照の正しい動作を推測しようとはしません。クラスは、たとえば、そのスレッド ローカル ストレージを存続させることができます。

于 2011-10-06T02:24:06.607 に答える
1

これは、ThreadLocal<>とメモリリークに関連しています

私の推測では、にIDisposable制約がないためT、のユーザーはThreadLocal<T>適切な場合にローカルオブジェクトを破棄すると想定されます。

于 2011-10-06T02:41:28.137 に答える
1

ThreadLocal.Dispose メソッド自体はどのように呼び出されますか? 「using」ブロックのようなものにある可能性が最も高いと思います。ThreadLocal の「using」ブロックを、そこに格納されるリソースの「using」ブロックでラップすることをお勧めします。

于 2011-10-06T19:58:54.183 に答える
1

MSDN のリファレンスでは、ThreadLocal 値は、処理が完了したら、それらを使用するスレッドによって破棄されるべきであると述べています。ただし、スレッド プールを使用したイベント スレッドなどの一部のインスタンスでは、スレッドが値を使用して別の処理を行った後、その値に N 回戻ってくる場合があります。

具体的な例としては、一連のサービス バス ワーカー スレッドの存続期間にわたって Entity Framework DBContext を持続させたい場合があります。

これらのインスタンスで使用する次のクラスを作成しました。 DisposeThreadCompletedValues を別のスレッドによって手動で頻繁に呼び出すか、内部モニター スレッドをアクティブにすることができます。

うまくいけば、これは役に立ちますか?

using System.Threading;
public class DisposableThreadLocal<T> : IDisposable
    where T : IDisposable
{
    public DisposableThreadLocal(Func<T> _ValueFactory)
    {
        Initialize(_ValueFactory, false, 1);
    }
    public DisposableThreadLocal(Func<T> _ValueFactory, bool CreateLocalWatcherThread, int _CheckEverySeconds)
    {
        Initialize(_ValueFactory, CreateLocalWatcherThread, _CheckEverySeconds);
    }

    private void Initialize(Func<T> _ValueFactory, bool CreateLocalWatcherThread, int _CheckEverySeconds)
    {
        m_ValueFactory = _ValueFactory;
        m_CheckEverySeconds = _CheckEverySeconds * 1000;
        if (CreateLocalWatcherThread)
        {
            System.Threading.ThreadStart WatcherThreadStart;
            WatcherThreadStart = new ThreadStart(InternalMonitor);
            WatcherThread = new Thread(WatcherThreadStart);
            WatcherThread.Start();
        }
    }

    private object SyncRoot = new object();

    private Func<T> m_ValueFactory;
    public Func<T> ValueFactory
    {
        get
        {
            return m_ValueFactory;
        }
    }

    private Dictionary<Thread, T> m_InternalDict = new Dictionary<Thread, T>();
    private Dictionary<Thread, T> InternalDict
    {
        get
        {
            return m_InternalDict;
        }
    }

    public T Value
    {
        get
        {
            T Result;
            lock(SyncRoot)
            {
                if (!InternalDict.TryGetValue(Thread.CurrentThread,out Result))
                {
                    Result = ValueFactory.Invoke();
                    InternalDict.Add(Thread.CurrentThread, Result);
                }
            }
            return Result;
        }
        set
        {
            lock (SyncRoot)
            {
                if (InternalDict.ContainsKey(Thread.CurrentThread))
                {
                    InternalDict[Thread.CurrentThread] = value;
                }
                else
                {
                    InternalDict.Add(Thread.CurrentThread, value);
                }
            }
        }
    }

    public bool IsValueCreated
    {
        get
        {
            lock (SyncRoot)
            {
                return InternalDict.ContainsKey(Thread.CurrentThread);
            }
        }
    }

    public void DisposeThreadCompletedValues()
    {
        lock (SyncRoot)
        {
            List<Thread> CompletedThreads;
            CompletedThreads = new List<Thread>();
            foreach (Thread ThreadInstance in InternalDict.Keys)
            {
                if (!ThreadInstance.IsAlive)
                {
                    CompletedThreads.Add(ThreadInstance);
                }
            }
            foreach (Thread ThreadInstance in CompletedThreads)
            {
                InternalDict[ThreadInstance].Dispose();
                InternalDict.Remove(ThreadInstance);
            }
        }
    }

    private int m_CheckEverySeconds;
    private int CheckEverySeconds
    {
        get
        {
            return m_CheckEverySeconds;
        }
    }

    private Thread WatcherThread;

    private void InternalMonitor()
    {
        while (!IsDisposed)
        {
            System.Threading.Thread.Sleep(CheckEverySeconds);
            DisposeThreadCompletedValues();
        }
    }

    private bool IsDisposed = false;
    public void Dispose()
    {
        if (!IsDisposed)
        {
            IsDisposed = true;
            DoDispose();
        }
    }
    private void DoDispose()
    {
        if (WatcherThread != null)
        {
            WatcherThread.Abort();
        }
        //InternalDict.Values.ToList().ForEach(Value => Value.Dispose());
        foreach (T Value in InternalDict.Values)
        {
            Value.Dispose();
        }
        InternalDict.Clear();
        m_InternalDict = null;
        m_ValueFactory = null;
        GC.SuppressFinalize(this);
    }
}
于 2016-10-26T02:11:05.687 に答える