Home > other >  killing a long running thread that is blocking on another child process to end
killing a long running thread that is blocking on another child process to end

Time:07-22

So, a little background. I have a program that creates a child process that runs long term and does some processing that we don't really care about for this question. It exists, and it needs to keep existing. So after starting that child process I start a thread that watches that child process and blocks waiting for it to end by Process.WaitForExit() and if it ends, it will restart the child process and then wait again. Now the problem is, how do I gracefully shut all of this down? If I kill the child process first, the thread waiting on it will spin it up again, so I know that the watcher thread needs to be killed first. I have been doing this by Thread.Abort() and then just catching the ThreadAbortException and returning ending the watcher thread and then I kill my child process. But I have been told that Thread.Abort() should be avoided at all costs and is possibly no longer supported in .Net core? So my question is why is Thread.Abort() so dangerous if I am catching the ThreadAbortException? and what is the best practice for immediately killing that thread so it doesn't have a chance to spin up the child thread again during shut down?

CodePudding user response:

What you are looking for is way to communicate across threads. There are multiple ways to do this but they all have specific conditions applicable.

For example mutex and semaphore are available across processes. events or wait handles are specific to a given process, etc. Once you know the details of these you can use them to send signal from one thread to another.

A simple setup for your requirement can be -

  1. Create a resetevent before spawning any of your threads.
  2. Let the child thread begin. In your parent wait on the reset event that you have created.
  3. Let the child thread reset the event.
  4. In your parent thread the wait state is completed, you can take further actions, such as kicking of the thread again and waiting on it or simply cleaning up and walking out of execution.

Thread.Abort is an unclean way of finishing your processing. If you read the msdn article here - https://docs.microsoft.com/en-us/dotnet/api/system.threading.thread.abort?view=net-6.0 the remark clearly tells you that you cant be sure what current state your thread execution was in. Your thread may not get opportunity to follow up with important clean up tasks, such as releasing resources that it does not require no more.

This can also lead to deadlock if you have more complicated constructs in place, such as thread being aborted doing so from protected region of code, such as a catch block or a finally block. If the thread that calls Abort holds a lock that the aborted thread is waiting on, a deadlock can acquire.

Key to remember in multithreading is that it is your responsibility to let the logic have a clean way of reaching to completion and finish thread's execution.

Please note that steps suggested above is one way of doing it. Depending on your requirements it can be restructured/imporved further. For example, if you are spawning another process, you will require kernel level objects such as mutex or semaphore. Objects like event or flag cant work across the process.

Read here - https://docs.microsoft.com/en-us/dotnet/standard/threading/overview-of-synchronization-primitives for more information.

CodePudding user response:

You should change the waiting thread to use async instead. For example, you can do something like this.

static async Task RunProcessWithRestart()
{
    using cancel = new CancellationTokenSource();
    try
    {
        while (true)
        {
            using (var process = CreateMyProcessAndStart())
            {
                await process.WaitForExitAsync(cancel.Token);
            }
        }
    }
    catch(OperationCanceledException)
    {
    }
}

static CancellationTokenSource cancel;

public static void StartWaitForProcess()
{
    Task.Run(RunProcessWithRestart);
}

public static void ShutdownWaitForProcess()
{
    cancel.Cancel();
}

An alternative, which doesn't require calling Cancel() from a separate shutdown function, is to subscribe to the AppDomain.ProcessExit event.

static async Task RunProcessWithRestart()
{
    using var cancel = new CancellationTokenSource();
    AppDomain.ProcessExit  = (s, e) => cancel.Cancel();
    try
    {
        while (true)
        {
            using (var process = CreateMyProcessAndStart())
            {
                await process.WaitForExitAsync(cancel.Token);
            }
        }
    }
    catch(OperationCanceledException)
    {
    }
}

public static void StartWaitForProcess()
{
    Task.Run(RunProcessWithRestart);
}
  • Related