繁体   English   中英

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

[英]C# - Thread Abort Exception (Thread Abort Exception) rethrowing itself

我有当前的代码:

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语句正在捕获它,但我的问题是:

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

用户可以做到

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

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

MSDN链接

ThreadAbortException重新抛出本身的要点是确保线程终止,除非用户显式调用ResetAbort

让我解释:

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

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

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

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 ,这使事情变得更糟。

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

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

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

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

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

考虑这个例子:

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

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

同时..你的用户点击退出程序 - 他做了那个,而线程“A”仍在执行中。

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

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

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

  2. 从线程“A” 完成任务 ,然后退出..

  3. 回滚线程“A”的最新指令,然后才退出..

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

所以,除非你真的很谦虚......你可能会选择选项2或选项3。

但假设你的退出方法使用了Thread.Abort()
您将需要使用Thread.ResetAbort()来“覆盖”并完成您的目标。

简而言之 - 它是衡量“优雅”的标准,让您可以更好地控制在某些情况下发生的事情。

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

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

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

否则 - 没有必要使用它。

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

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

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

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的博客条目, 托管代码和异步异常强化之一中 ,他谈到了ResetAbort和Abort API:

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM