处理ThreadLocal <IDisposable>中保存的元素的正确方法是什么?

Sco*_*ain 25 c# idisposable .net-4.0 thread-local

当你使用a ThreadLocal<T>和T实现IDisposable时,你应该如何处理ThreadLocal中的成员?

根据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;
}
Run Code Online (Sandbox Code Playgroud)

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!");
    }
}
Run Code Online (Sandbox Code Playgroud)

Eni*_*ity 12

我看了一下代码,ThreadLocal<T>看看目前的情况Dispose是什么,看起来很多伏都教.显然处理与线程相关的东西.

但如果T它本身是一次性的,它不会处置这些值.

现在,我有一个解决方案 - 一个ThreadLocalDisposables<T>类,但在我给出完整的定义之前,值得考虑如果你编写这个代码会发生什么:

var tl = new ThreadLocalDisposables<IExpensiveDisposableResource>();
tl.Value = myEdr1;
tl.Value = myEdr2;
tl.Dispose();
Run Code Online (Sandbox Code Playgroud)

都应该myEdr1myEdr2同时配置?还是只是myEdr2?或者myEdr1myEdr2分配时应该处理?

我不清楚语义应该是什么.

但是,我很清楚,如果我写了这段代码:

var tl = new ThreadLocalDisposables<IExpensiveDisposableResource>(
    () => new ExpensiveDisposableResource());
tl.Value.DoSomething();
tl.Dispose();
Run Code Online (Sandbox Code Playgroud)

然后我希望工厂为每个线程创建的资源应该被处理掉.

所以我不会允许直接分配一次性值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; }
    }
}
Run Code Online (Sandbox Code Playgroud)

这有帮助吗?


Chu*_*huu 7

在 .NET 4.5 中,Values属性被添加到 ThreadLocal<> 以处理手动管理 ThreadLocal 对象的生命周期的问题。它返回绑定到该 ThreadLocal 变量的所有当前实例的列表。

这篇 MSDN 文章中提供了一个使用 Parallel.For 循环访问 ThreadLocal 数据库连接池的示例。相关代码片段如下。

var threadDbConn = new ThreadLocal<MyDbConnection>(() => MyDbConnection.Open(), true);
try
{
    Parallel.For(0, 10000, i =>
    {
        var inputData = threadDbConn.Value.GetData(i);
        ...
    });
}
finally
{
    foreach(var dbConn in threadDbConn.Values)
    {
        dbConn.Close();
    }
}
Run Code Online (Sandbox Code Playgroud)