Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the correct way to dispose elements held inside a ThreadLocal<IDisposable>?

When you use a ThreadLocal<T> and T implements IDisposable, how are you supposed to dispose of the members being held inside of the ThreadLocal?

According to ILSpy, the Dispose() and Dispose(bool) methods of ThreadLocal are

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; } 

It does not appear that ThreadLocal attempts to call Dispose on its child members. I can't tell how to reference each thread it internally has allocated so I can take care of it.


I ran a test with the following code, the class is never disposed

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!");     } } 
like image 812
Scott Chamberlain Avatar asked Oct 06 '11 02:10

Scott Chamberlain


People also ask

How to dispose a Thread in c#?

In C#, a thread can be terminated using the Abort() method. The Abort() throws ThreadAbortException to the thread in which it is called. Due to this exception, the thread is terminated.

What does the Dispose method do?

Dispose improves performance and optimizes memory by releasing unmanageable objects and scarce resources, like Graphics Device Interface (GDI) handles used in applications with restricted Windows space. The Dispose method, provided by the IDisposable interface, implements Dispose calls.

When the Dispose method is called?

The Dispose() methodThe Dispose method performs all object cleanup, so the garbage collector no longer needs to call the objects' Object. Finalize override. Therefore, the call to the SuppressFinalize method prevents the garbage collector from running the finalizer. If the type has no finalizer, the call to GC.


1 Answers

I had a look at the code in ThreadLocal<T> to see what the current Dispose is doing and it appears to be a lot of voodoo. Obviously disposing of thread-related stuff.

But it doesn't dispose of the values if T itself is disposable.

Now, I have a solution - a ThreadLocalDisposables<T> class, but before I give the full definition it's worth thinking about what should happen if you wrote this code:

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

Should both myEdr1 & myEdr2 both be disposed? Or just myEdr2? Or should myEdr1 be disposed when myEdr2 was assigned?

It's not clear to me what the semantics should be.

It is clear to me, however, that if I wrote this code:

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

Then I would expect that the resource created by the factory for each thread should be disposed of.

So I'm not going to allow the direct assignment of the disposable value for ThreadLocalDisposables and only allow the factory constructor.

Here's 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; }     } } 

Does this help?

like image 184
Enigmativity Avatar answered Oct 05 '22 23:10

Enigmativity