C# - 线程中止异常(线程中止异常)重新抛出自身

时间:2014-06-20 14:31:26

标签: c# multithreading exception

我有当前的代码:

class Program
{
    private static void Main()
    {
        while (true)
        {

            try
            {
                Thread.CurrentThread.Abort();
            }
            catch (ThreadAbortException)
            {
                Console.WriteLine("Abort!");
                Thread.ResetAbort();
            }


            Console.WriteLine("now waiting");
            Console.ReadKey();
        }
    }
}

现在我知道方法ResetAbort应该阻止ThreadAbortException继续重新抛出自己,即使catch语句正在捕获它,但我的问题是:< / p>

如果有人可以使用ResetAbort方法,那么异常的重点是什么?

用户可以做到

    catch (ThreadAbortException ex)
    {
        Console.WriteLine("Abort!");
        throw ex;
    }

5 个答案:

答案 0 :(得分:8)

Thread.ResetAbort()不适合常用。如果您不理解为什么中止发生,它可能会导致不良行为。因此,可能要使ASP.NET在共享托管环境中保持稳定,需要SecurityPermissionFlag.ControlThread权限才能调用Thread.ResetAbort()

MSDN Link

答案 1 :(得分:6)

ThreadAbortException重新抛出本身的目的是确保线程终止,除非用户明确调用ResetAbort

让我解释一下:

try
{
    // ... Thread abort happens somewhere in here
}
catch (Exception ex)
{
    _log.Error(ex);
}

这里有一个典型的代码示例,可确保try块内没有异常传播。我知道捕捉Exception是不好的做法,但是这样的代码仍然存在。

如果在线程位于Abort块内时调用try,您仍希望它中止。您无法依赖用户在任何地方编写此类代码:

try
{
    // ... Thread abort happens somewhere in here
}
catch (ThreadAbortException)
{
    throw; // No, you'll NEVER see code like this in real life
}
catch (Exception ex)
{
    _log.Error(ex);
}

因此,为了提供可靠Abort,必须自动重新抛出异常,否则很容易被意外丢弃。

ResetAbort适用于非常罕见的情况,当您专门检测线程中止时,您确切知道它为什么会发生,并且您想要阻止它。

毋庸置疑,此案例的用例极为罕见。线程中止由运行时以非常特殊的方式处理,您应该尽可能地避免它们。哎呀,他们甚至不像你指出的那样可靠,所有这些讨论都忽略了CER,这使事情变得更糟。

答案 2 :(得分:2)

重点是定义一个默认行为,在该行为中重新抛出异常,看看用户是否有可能有任何继续线程的点。

此外,ResetAbort具有安全性要求,任何代码都无法调用。

答案 3 :(得分:1)

除非你的目标是创建一个表现出认知扭曲或分裂人格行为的应用程序或某种自我转化/变异代码..(这可能是一个非常酷的表达 - 我知道.. :))

线程没有必要中止自己,而不是捕获抛出的异常并通过重置堕胎或重新投掷它来管理它。

我认为您使用错误的方案来理解这些说明的原因。

考虑这个例子:

假设您正在开发涉及数据库信息的多线程应用程序。

并且......让我们说您的用户指示做一些与处理和保存数据到您的数据库相关的批处理作业。
而且......这个任务 - 让我们说需要30秒才能完成 所以 - 你为它打开一个线程并在后台管理它。 (让我们称之为线程&#34; A&#34;)

同时.. 你的用户点击退出程序 - 他在线程&#34; A&#34;仍在执行中。

现在,你的退出方法 - 没有高度管理..
它仅用于简单地中止应用程序打开的所有正在运行的线程 它不知道每个线程做什么..

那么现在该怎么办? - 有三个学科:

  1. 立即退出 - 用户是神圣的!如果那就是他想要的 - 我是谁来质问他。

  2. 从线程中完成任务&#34; A&#34;然后才退出..

  3. 回滚线程的最新指令&#34; A&#34;然后才退出..

  4. 在选项1中,由于用户操作,数据库完整性可能会受到影响。
    在选项2&amp; 3您优先考虑逻辑流程,以牺牲对用户请求的响应能力。

    所以,除非你真的很谦虚......你可能会选择选项2或选项3
    但假设您的退出方法使用Thread.Abort()
    您需要使用Thread.ResetAbort()来&#34;覆盖&#34;那就完成了你的目标。

    很短暂 - 它是衡量&#34;恩典&#34;让您更好地控制在某些情况下发生的事情。

    通常,良好的做法将允许退出方法知道发生了什么。
    通常,良好的做法根本不需要中止一个线程(它自己打开)。

    结论, - 很高兴知道它在那里......并且没有 - 使用它很糟糕。

    除非您要在未打开的主题上使用Thread.Abort()(可能 - 复杂..),
    或者期望从外部中止,因此需要Thread.ResetAbort()

    否则 - 无需使用它。

答案 4 :(得分:1)

因为中止线程并不一定意味着会抛出异常。对于中止过程catch (ThreadAbortException)块只是代码的另一个关键区域。它只为我们提供了一种线程安全且方便的方法来检测当前线程是否被中止(并且可能还有一些状态被传递)以防我们想要做一些特别的事情。除此之外,它就像任何其他关键区域(如finally块),它将在执行后终止线程。

同时,在您的示例中,Abort被称为同步(这实际上是安全的),在这种情况下 非常类似于抛出一个例外。当它从另一个线程被称为异步时,事情变得有趣和危险,因为Abort过程比抛出异常更复杂:本质上,首先,线程被标记为中止,然后是关键代码如果仍然在线程上设置 AbortRequested 标志,则只执行区域(例如finally块),然后抛出异常,依此类推。

下面的代码通过恢复中止的线程而不会出现任何异常来说明这一事实:

var inFinally = new ManualResetEvent(false);
var abortCalled = new ManualResetEvent(false);

var t = new Thread(_ =>
{
    Console.WriteLine("Thread started..");
    try
    {
    }
    finally
    {
        inFinally.Set();
        abortCalled.WaitOne();
        Console.WriteLine(" ThreadState (before): " + Thread.CurrentThread.ThreadState);

        // This isn't thread safe, and ugly?
        if ((Thread.CurrentThread.ThreadState & ThreadState.AbortRequested) != 0)
        {
            Thread.ResetAbort();
        }

        Console.WriteLine(" ThreadState (after): " + Thread.CurrentThread.ThreadState);
    }
    Console.WriteLine("Executed because we called Thread.ResetAbort()");
});

t.Start();

inFinally.WaitOne();

// Call from another thread because Abort()
// blocks while in finally block
ThreadPool.QueueUserWorkItem(_ => t.Abort());

while ((t.ThreadState & ThreadState.AbortRequested) == 0)
{
    Thread.Sleep(1);
}

abortCalled.Set();

Console.ReadLine();

//  Output:
//--------------------------------------------------
// Thread started..
//  ThreadState (before): AbortRequested
//  ThreadState (after): Running
// Executed because we called Thread.ResetAbort()

现在,我必须诚实:我不完全确定如何使用此功能并创建有用的东西。但听起来好像Thread.Abort API(可能仍然是,我不知道)用于在ASP.NET等框架中促进线程和AppDomain的重用。

在Joe Duffy的博客文章Managed code and asynchronous exception hardening中,他谈到了ResetAbort和Abort API:

  

某些框架基础结构,尤其是ASP.NET,甚至可以中止单个线程   通常不卸载域名。他们支持ThreadAbortExceptions,调用   ResetAbort在线程上并重用它或将其返回给CLR ThreadPool。

我可以想象它可以在框架中用于重用托管线程,从而减少开销。但是,用户代码中引入的问题(错误的线程同步设计,错误的异常处理,死锁等),通过这个容易被误解的API,使得Abort和ResetAbort调用比使用更麻烦。