捕获ThreadAbortException时隐藏的Throw有什么用处?

时间:2010-05-13 21:19:12

标签: c# multithreading

我正在阅读一本关于c#开发的一般书,我来到线程中止部分。

这本书说明了当你在另一个线程上调用Thread.Abort()时,该线程会抛出一个ThreadAbortException,即使你试图压制它也会自动重新抛出它,除非你做了一些bs一般不赞成。这是提供的简单示例。

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!");
             }
        }
    }

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

这本书说:

当您的线程完成处理中止异常时,运行时会在异常处理程序的末尾隐式重新生成它。就像你自己重新抛出异常一样。因此,任何外部异常处理程序或finally块仍将正常执行。在该示例中,对Join的调用将不会像最初预期的那样永远等待。

所以我在Thread.Abort()调用周围包装了一个try catch并设置了一个断点,期望它达到这一点,考虑到文本说“任何外部异常处理程序或最终块仍将正常执行”。但它没有。我绞尽脑汁想出原因。

任何人都有任何想法为什么不是这种情况?这本书错了吗?

提前致谢。

5 个答案:

答案 0 :(得分:8)

在中止的线程上抛出异常。一旦抛出异常就会向该线程的调用堆栈移动,但它不会跳转到另一个线程,也不会跳转到Thread.Abort的调用者。

索赔是:

  

因此,任何外部异常处理程序或finally块仍将正常执行。

对此声明的更好测试是以下代码:

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?");
        }
    }
}

如果ThreadAbortException是正常类型的异常,我们就不会指望"Do we get here?"行,但我们会这样做。

答案 1 :(得分:1)

Thread.Abort()不会抛出异常。相反,线程中会抛出异常,即使您捕获它,也会立即重新抛出。在你的情况下,它会在打印“Abort!”之后立即重新出现。继续将你的线程方法的主体包装在另一个try / catch中,你将能够确认这一点。

答案 2 :(得分:1)

你可能会眨眼睛看得太快。像这样修改你的代码:

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

可能的输出:

....
8807
8808
Abort!

答案 3 :(得分:1)

那本书应该说的是你永远不应该使用Thread.Abort,因为它有很多问题。

因此,您看到的任何非显而易见或意外的行为都应被视为您不应该使用Thread.Abort

Thread.Abort的意思是向一个线程发出信号,表明应用程序正在终止,你未能很好地退出(我已经问过),现在你必须死掉。对此感到抱歉,但就是这样。

就是这样。您可以想到的所有其他用例都涉及到Thread.Abort应该有一个不同的解决方案,周期。

合作方法通常是最好的方法。使用信号(即使是像易失性布尔字段那样简单的东西),只需在线程退出时设置信号即可。在另一个线程中定期检查此信号,设置后退出。或者......如果您检测到信号,甚至会抛出异常。

从不使用Thread.Abort从外部强加该异常。

现在,如果你真的想知道如何处理这个异常,你可以向运行时发出信号,告诉你不希望异常通过调用自动向上传播(ThreadAbortException是一种特殊情况) Thread.ResetAbort()

但是,你不应该这样做。

答案 4 :(得分:0)

当你启动线程时,你的代码继续运行,你启动的线程是单独运行的,也就是为什么我们不应该期望在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());
       }
    }
}

记住ThreadAbortException是一个v。特殊异常,因为出于所有目的,该线程意味着死亡。当你绝对终止你的应用并且线程中没有任何特殊内容时,这很好。如果您希望正常终止操作,请不要使用Thread.Abort ,使用单独的机制来告诉它需要停止的线程。就像在示例中一样,设置一个静态成员,它会检查while,下一个周期它知道它必须在受控制的地方停止。