3

I have an WPF application that uses many third-party DLLs which are working in their own threads.

Some of those DLLs have method STOP().

I am using Thread.Sleep(1000) after each SomeObject.Stop() method...

Anyway when I terminate application some of the threads are still in memory.

Any clue how to resolve this problem?

NoWar
  • 36,338
  • 80
  • 323
  • 498

2 Answers2

5

So basically you're facing an issue with a 3rd party library that doesn't nicely clean up when it should, and on top of that its spun up a bunch of foreground threads keeping your application running even when you want it terminated.

Example

As an example, an application like this

private static void Main(string[] args)
{

    var t = new Thread(() =>
        {
            while (true)
            {
            }
        }) {Name = "test"};

    t.Start();

    Console.WriteLine("Exited");
}

Will sit forever since t is a foreground thread.

Let's just double check with process explorer. Here's an updated version of our demo app that we can pinvoke and get the native thread id.

internal class Program
{
    [DllImport("kernel32.dll")]
    static extern IntPtr OpenThread(uint dwDesiredAccess, bool bInheritHandle, uint dwThreadId);


    [DllImport("kernel32.dll")]
    static extern bool TerminateThread(IntPtr hThread, uint dwExitCode);

    [DllImport("kernel32.dll")]
    static extern int GetCurrentThreadId();

    private static void Main(string[] args)
    {

        var t = new Thread(() =>
            {
                Console.WriteLine(GetCurrentThreadId());
                while (true)
                {
                }
            }) {Name = "test"};

        t.Start();

        Console.WriteLine("Thread Id " + t.ManagedThreadId);
        Console.WriteLine("Exited");

    }
}

Gives me the native thread id. In process explorer I can see now:

spinning thread

Pretty clear that thread 8228 is spinning wildly because of my while loop, even though main has already exited

In general, User Rob Hardy is right. If you control your threads you should always keep track of things and manage them yourself, but I think you're in a pickle here since you don't have access to the thread handles.

Option 1 (but please don't)

You can try and kill everything (but that didn't really work for me when I tried it anyways), but I really wouldn't do this since it seems incredibly dangerous to do. Just to copy the information from the other post the example said this:

internal class Program
{
    [System.Runtime.InteropServices.DllImport("kernel32.dll")]
    static extern IntPtr OpenThread(uint dwDesiredAccess, bool bInheritHandle, uint dwThreadId);


    [System.Runtime.InteropServices.DllImport("kernel32.dll")]
    static extern bool TerminateThread(IntPtr hThread, uint dwExitCode);

    private static void Main(string[] args)
    {

        var t = new Thread(() =>
            {
                while (true)
                {
                }
            }) {Name = "test"};

        t.Start();

        Console.WriteLine("Exited");

        Thread.Sleep(TimeSpan.FromSeconds(2));

        foreach (ProcessThread pt in Process.GetCurrentProcess().Threads)
        {
            IntPtr ptrThread = OpenThread(1, false, (uint)pt.Id);
            if (AppDomain.GetCurrentThreadId() != pt.Id)
            {
                try
                {
                    TerminateThread(ptrThread, 1);
                    Console.Out.Write(". Thread killed.\n");
                }
                catch (Exception e)
                {
                    Console.Out.WriteLine(e.ToString());
                }
            }
            else
                Console.Out.Write(". Not killing... It's the current thread!\n");
        }
    }
}

But again, that didn't stop the process for me. Maybe .net is waiting for proper exiting of the threads and not just forceful native exits? I don't know. I'm just showing that you can technically (according to the link) can kill threads from ProcessThread, if you REALLY wanted to (but please don't)

Option 2

A more reasonable option is to add an explicit Exit call with a code after all your cleanup (exit code 0 is customary to indicate a clean exit)

private static void Main(string[] args)
{

    var t = new Thread(() =>
        {
            while (true)
            {
            }
        }) {Name = "test"};

    t.Start();

    Console.WriteLine("Exited");

    Environment.Exit(0);
}

This worked for me.

Option 3

The third option, if you can, is to just fix the library and have it either create background threads or let it clean up properly. This would be the best option since you won't be leaving any potential corruptible side effects open by not cleaning up the 3rd party items. Though, I'm assuming you aren't doing this because the library is closed source.

devshorts
  • 8,572
  • 4
  • 50
  • 73
0

Keep track of your threads by adding them to a collection, or using the ThreadPool

In each thread make sure that you handle the ThreadAbortException

At the termination point at the end of your Main(), go through your collection of Threads and call Abort() on each one.

Rob Hardy
  • 1,821
  • 15
  • 15
  • 4
    I think the issue he's having is that these threads are not created by him, but by internals of whatever library he's using – devshorts Feb 13 '13 at 12:49
  • I cannot do it. All those threads are some DLLs and I have only public nethods like START/STOP. No way to track it like u said. – NoWar Feb 13 '13 at 12:50
  • 2
    In that case, he could try the Process.Threads collection which will get all Threads owned by the current Process, as documented here: http://msdn.microsoft.com/en-us/library/system.diagnostics.process.threads(v=vs.80).aspx – Rob Hardy Feb 13 '13 at 12:50