开发者

What's the deal with the hidden Throw when catching a ThreadAbortException?

开发者 https://www.devze.com 2022-12-30 00:54 出处:网络
I\'m going through a book of general c# development, and I\'ve come to the thread abort section. The book says something along the lines that when you call Thread.Abort() on another thread, that thre

I'm going through a book of general c# development, and I've come to the thread abort section.

The book says something along the lines that when you call Thread.Abort() on another thread, that thread will throw a ThreadAbortException, and even if you tried to supress it it would automatically rethrow it, unless you did some bs that's generally frowned upon. Here's the simple example offered.

using System;
using System.Threading;

public class EntryPoint
{
    private static void ThreadFunc()
    {
        ulong counter = 0;
        while (true)
        {
            try
            {
                Console.WriteLine("{0}", counter++);
            }
            catch (ThreadAbortException)
            {
                // Attempt to swallow the exception and continue.
                Console.WriteLine("Abort!");
             }
        }
    }

    开发者_StackOverflow社区static void Main()
    {
        try
        {
            Thread newThread = new Thread(new ThreadStart(EntryPoint.ThreadFunc));
            newThread.Start();
            Thread.Sleep(2000);

            // Abort the thread.
            newThread.Abort();

           // Wait for thread to finish.
           newThread.Join();
        }
       catch (Exception e)
       {
           Console.WriteLine(e.ToString());
       }
    }
}

The book says:

When your thread finishes processing the abort exception, the runtime implicitly rethrows it at the end of your exception handler. It’s the same as if you had rethrown the exception yourself. Therefore, any outer exception handlers or finally blocks will still execute normally. In the example, the call to Join won’t be waiting forever as initially expected.

So i wrapped a try catch around the Thread.Abort() call and set a break point, expecting it to hit this, considering the text says "any outer exception handlers or finally blocks will still execute normally". BUT IT DOES NOT. I'm racking my brain to figure out why.

Anyone have any thoughts on why this isn't the case? Is the book wrong?

Thanks in advance.


The exception is thrown on the thread that is aborted. Once thrown an exception travels up the call stack of that thread, but it does not jump over to another thread, nor to the caller of Thread.Abort.

The claim was:

Therefore, any outer exception handlers or finally blocks will still execute normally.

A better test of this claim is the following code:

private static void ThreadFunc()
{
    ulong counter = 0;
    while (true)
    {
        try
        {
            try
            {
                Console.WriteLine("{0}", counter++);
            }
            catch (ThreadAbortException)
            {
                // Attempt to swallow the exception and continue.
                Console.WriteLine("Abort!");
            }
        }
        catch (ThreadAbortException)
        {
            Console.WriteLine("Do we get here?");
        }
    }
}

If ThreadAbortException were a normal type of exception we would not expect to hit the line "Do we get here?", but we do.


The Thread.Abort() doens't throw the exception. Rather, an exception is thrown in the thread, and even if you catch it, is is immediately rethrown. In your case, it would be rethrown right after it prints "Abort!". Go ahead and wrap the body of your thread method in another try/catch and you'll be able to confirm this.


You probably blinked your eyes too fast to see it. Modify your code like this:

       // Wait for thread to finish.
       newThread.Join();
       Console.ReadLine();

Possible output:

....
8807
8808
Abort!


What that book should've said is that you should never use Thread.Abort because it has a massive amount of problems.

As such, any non-obvious or unexpected behaviour you see should be taken as an indication that you really should not use Thread.Abort.

The point of Thread.Abort is to signal to a thread that the application is terminating, you have failed to exit nicely (I have asked), and now you have to die. Sorry about that, but that's just the way it is.

That is all. Every other use-case you can think of that would involve Thread.Abort should have a different solution, period.

A cooperative method is usually the best way to do it. Use a signal (even something as simple as a volatile boolean field), and just set the signal when you want the thread to exit. Periodically check this signal in the other thread, and when set, exit. Or... even throw an exception if you detect the signal.

Just never impose that exception from the outside using Thread.Abort.

Now, having said that, if you really want to know how to handle this exception, you can signal to the runtime that you don't want the exception to automatically propagate up the stack (ThreadAbortException is a special case) by calling Thread.ResetAbort().

However, you should not do this.


When you start the thread, your code goes on and the thread you started is running separately i.e. why we shouldn't expect to hit that line in main.

using System;
using System.Threading;

public class EntryPoint
{
    private static void ThreadFunc()
    {
        try
        {
            ulong counter = 0;
            while (true)
            {
                try
                {
                    Console.WriteLine("{0}", counter++);
                }
                catch (ThreadAbortException)
                {
                    // Attempt to swallow the exception and continue.
                    Console.WriteLine("Abort!");
                 }
            }
        }
        catch(ThreadAbortException)
        {
            Console.WriteLine("Certainly unstoppable!");
        }
    }

    static void Main()
    {
        try
        {
            Thread newThread = new Thread(new ThreadStart(EntryPoint.ThreadFunc));
            newThread.Start();
            Thread.Sleep(2000);

            // Abort the thread.
            newThread.Abort();

           // Wait for thread to finish.
           newThread.Join();
        }
       catch (Exception e)
       {
           Console.WriteLine(e.ToString());
       }
    }
}

Remember ThreadAbortException its a v. special exception, since for all purposes the thread is meant to be dead. That's fine when you are absolutely terminating your app and don't have anything special in the thread. When instead you want to gracefully terminate operations, don't use Thread.Abort, use a separate mecanism to tell the thread it needs to stop. Like in the example, setting a static member it checks in the while, the next cycle it knows it has to stop in a controlled place.

0

精彩评论

暂无评论...
验证码 换一张
取 消