Monster Hunter
Monster Hunter

Reputation: 866

How to implicitly stop a thread in an object in C#

I have an object that contains a working thread. I'd like to kill the thread when the object is out of scope.

using System.IO;
using System;
using System.Threading;

namespace tt {
    class Program
    {
        static void Main()
        {
            AnotherClass a = new AnotherClass();
            a.Say();
        }

    }

    class AnotherClass:IDisposable {
        private bool m_Disposed;
        private readonly AutoResetEvent m_ResetEvent = new AutoResetEvent(false);

        public AnotherClass() {
            Thread t = new Thread(wait);
            t.Start();
        }

        public void Dispose() {
            Dispose(true);
        }
        private void Dispose(bool disposing) {
            if (m_Disposed) {
                return;
            }

            if (disposing) {
                Console.WriteLine("inner disposing");
            }
            m_ResetEvent.Set();
            Console.WriteLine("Outer disposing");
            m_Disposed = true;
        }

        private void wait() {
            m_ResetEvent.WaitOne();
        }

        ~AnotherClass() {
            Dispose(false);
        }

        public void Say() {
            Console.WriteLine("HellO");
        }
    }
}

The program will just hang in there because the destructor of AnotherClass is not called. I know I can call a.Dispose() to kill the thread. But is there a way to implicitly kill the thread when the object goes out of scope?

Upvotes: 6

Views: 2613

Answers (4)

supercat
supercat

Reputation: 81115

If your thread exists for the purpose of serving an object for the benefit of other threads, you should have a public-facing wrapper object to which all threads that are "interested" in the service will hold strong references, and the service itself will hold a long weak reference. That wrapper object should not hold any information that the service needs to read or manipulate, but should instead forward all requests for information to an object which both the wrapper and the service hold strong references.

When the server thread is awake, it should periodically check whether the weak reference to the wrapper object is still alive. If it isn't, the server thread should shut down in orderly fashion. Note that the server never uses a strong reference to the wrapper object--even temporarily--so the wrapper object should evaporate as soon as nobody is interested in it.

If the server thread may be blocked indefinitely waiting for various things to happen, the wrapper should hold the only reference anywhere in the universe to a finalizable object which holds a strong reference to the server object; when the finalizer fires, it should check whether the long weak reference held by the server object is still valid. If it isn't, it should try to nudge the server thread (e.g. using a Monitor.TryEnter/Monitor.Pulse. If the wrapper object is still alive (finalizers can occasionally false-trigger in some resurrection scenarios) or the finalizer wasn't able to nudge the server thread, the finalizable object should re-register for finalization.

Using the finalizer object will complicate things; if the server thread can awaken itself periodically without needing to be nudged, that will simplify the design. A finalizer used as described, however, should be relatively robust even in scenarios involving involuntary resurrection.

Upvotes: 1

sstan
sstan

Reputation: 36473

Adding to Guffa's answer, I think it's also important to note that even if you had a way to detect the precise moment that your AnotherClass instance went out out scope, in this case, it just never will. And that's why you noticed that your destructor never got called.

The reason for that is that when you create and start your working thread, the thread is passed a delegate reference pointing to the instance method wait() which comes with an implicit reference to this (the AnotherClass instance). So as long as your thread does not itself go out of scope, it will hold a strong reference to the AnotherClass instance and prevent it from getting garbage collected.

Upvotes: 2

Guffa
Guffa

Reputation: 700152

No, that's not possible. There is no reference counting that can notice that the object has no more references, so there is no way to do anything when that happens.

The IDisposable interface is used for classes that needs to do something when an instance is not going to be used any more, and calling the Dispose method is how you signal that you are done with the instance.

The usual way to make sure that an object is disposed when you leave a scope is to wrap the code in a using block:

static void Main()
{
  using (AnotherClass a = new AnotherClass())
  {
    a.Say();
  }
}

The using block is syntactic sugar for a try...finally block:

static void Main()
{
  AnotherClass a = new AnotherClass();
  try
  {
    a.Say();
  }
  finally
  {
    if (a != null)
    {
      ((Idisposable)a).Dispose();
    }
  }
}

Upvotes: 6

Mudassir Dehghani
Mudassir Dehghani

Reputation: 111

You could use exception handling and use the error displayed as the exception in the catch block

Upvotes: 1

Related Questions