简体   繁体   English

如何防止后台线程中的异常终止应用程序?

[英]How to prevent an exception in a background thread from terminating an application?

我可以连接到AppDomain.CurrentDomain.UnhandledException以记录来自后台线程的异常,但如何防止它们终止运行时?

First, you really should try not to have exceptions thrown - and not handled - in a background thread.首先,你真的应该尽量不要在后台线程中抛出异常——而不是处理——。 If you control the way your delegate is run, encapsulate it in a try catch block and figure a way to pass the exception information back to your main thread (using EndInvoke if you explicitly called BeginInvoke, or by updating some shared state somewhere).如果您控制委托的运行方式,请将其封装在 try catch 块中,并找出将异常信息传递回主线程的方法(如果您显式调用 BeginInvoke,则使用 E​​ndInvoke,或者通过更新某处的某些共享状态)。

Ignoring a unhandled exception can be dangerous.忽略未处理的异常可能很危险。 If you have a real un-handlable exception (OutOfMemoryException comes into mind), there's not much you can do anyway and your process is basically doomed.如果您有一个真正无法处理的异常(OutOfMemoryException 进入您的脑海),无论如何您都无能为力,您的过程基本上注定要失败。

Back to .Net 1.1, an unhandled exception in a backgroundthread would just be thrown to nowhere and the main thread would gladly plough on.回到 .Net 1.1,后台线程中未处理的异常只会被抛到任何地方,主线程会很乐意继续工作。 And that could have nasty repercussions.这可能会产生恶劣的影响。 So in .Net 2.0 this behavior has changed.所以在 .Net 2.0 中,这种行为发生了变化。

Now, an unhandled exception thrown in a thread which is not the main thread will terminate the process.现在,在不是主线程的线程中抛出的未处理异常将终止进程。 You may be notified of this (by subscribing to the event on the AppDomain) but the process will die nonetheless.您可能会收到通知(通过订阅 AppDomain 上的事件),但该过程仍将终止。

Since this can be inconvenient (when you don't know what will be run in the thread and you are not absolutely sure it's properly guarded, and your main thread must be resilient), there's a workaround.由于这可能很不方便(当您不知道将在线程中运行什么并且您不确定它是否得到适当保护时,并且您的主线程必须具有弹性时),有一个解决方法。 It's intended as a legacy settings (meaning, it's strongly suggested you make sure you don't have stray threads) but you can force the former behavior this way :它旨在作为遗留设置(意思是,强烈建议您确保没有杂散线程),但您可以通过这种方式强制前一种行为:

Just add this setting to your service/application/whatever configuration file :只需将此设置添加到您的服务/应用程序/任何配置文件:

<configuration>
  <runtime>
    <!-- the following setting prevents the host from closing when an unhandled exception is thrown -->
    <legacyUnhandledExceptionPolicy enabled="1" />
  </runtime>
</configuration>

It doesn't seem to work with ASP.NET, though.不过,它似乎不适用于 ASP.NET。

For more information (and a huge warning that this setting may not be supported in upcoming versions of the CLR) see http://msdn.microsoft.com/en-us/library/ms228965.aspx有关更多信息(以及即将发布的 CLR 版本可能不支持此设置的巨大警告),请参阅http://msdn.microsoft.com/en-us/library/ms228965.aspx

From Joe Albahari's excellent threading article:来自 Joe Albahari 的优秀线程文章:

The .NET framework provides a lower-level event for global exception handling: AppDomain.UnhandledException. .NET 框架为全局异常处理提供了一个较低级别的事件:AppDomain.UnhandledException。 This event fires when there's an unhandled exception in any thread, and in any type of application (with or without a user interface).当任何线程和任何类型的应用程序(有或没有用户界面)中出现未处理的异常时,将触发此事件。 However, while it offers a good last-resort mechanism for logging untrapped exceptions, it provides no means of preventing the application from shutting down – and no means to suppress the .NET unhandled exception dialog.然而,虽然它提供了一种很好的最后手段来记录未捕获的异常,但它没有提供防止应用程序关闭的方法——也没有提供抑制 .NET 未处理异常对话框的方法。

In production applications, explicit exception handling is required on all thread entry methods.在生产应用程序中,所有线程入口方法都需要显式异常处理。 One can cut the work by using a wrapper or helper class to perform the job, such as BackgroundWorker (discussed in Part 3).可以通过使用包装器或帮助器类来执行工作来减少工作,例如 BackgroundWorker(在第 3 部分中讨论)。

Keeping the answer short, yes, you do can prevent the runtime from terminating.保持简短,是的,您可以防止运行时终止。

Here is a demo of the workaround:这是解决方法的演示:

class Program
{
    void Run()
    {
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

        Console.WriteLine("Press enter to exit.");

        do
        {
            (new Thread(delegate()
            {
                throw new ArgumentException("ha-ha");
            })).Start();

        } while (Console.ReadLine().Trim().ToLowerInvariant() == "x");


        Console.WriteLine("last good-bye");
    }

    int r = 0;

    void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        Interlocked.Increment(ref r);
        Console.WriteLine("handled. {0}", r);
        Console.WriteLine("Terminating " + e.IsTerminating.ToString());

        Thread.CurrentThread.IsBackground = true;
        Thread.CurrentThread.Name = "Dead thread";            

        while (true)
            Thread.Sleep(TimeSpan.FromHours(1));
        //Process.GetCurrentProcess().Kill();
    }

    static void Main(string[] args)
    {
        Console.WriteLine("...");
        (new Program()).Run();
    }
}

Essentially, you just did not let the runtime to show the "... program has stopped working" dialog.本质上,您只是没有让运行时显示“...程序已停止工作”对话框。

In case you need to log the exception and silently exit, you can call Process.GetCurrentProcess().Kill();如果您需要记录异常并静默退出,您可以调用Process.GetCurrentProcess().Kill();

    AppDomain.CurrentDomain.UnhandledException += (sender, e2) =>
    {
        Thread.CurrentThread.Join();
    };

But be careful, this code will freeze all the stack memory of the Thread and thread's managed object itself.但是要小心,这段代码会冻结线程的所有堆栈内存和线程的托管对象本身。 However, if your application is in determined state (may be you threw LimitedDemoFunctionalityException or OperationStopWithUserMessageException) and you are not developing 24/7 application this trick will work.但是,如果您的应用程序处于确定状态(可能是您抛出了 LimitedDemoFunctionalityException 或 OperationStopWithUserMessageException)并且您不是在开发 24/7 应用程序,则此技巧将起作用。

Finally, I think MS should allow developers to override the logic of unhandled exceptions from the top of the stack.最后,我认为 MS 应该允许开发人员从堆栈顶部覆盖未处理异常的逻辑。

Here is a great blog post about this problem: Handling "Unhandled Exceptions" in .NET 2.0这是一篇关于这个问题的很棒的博客文章: Handling "Unhandled Exceptions" in .NET 2.0

IMO it would be right to handle exceptions in background threads manually and re-throw them via callback if necessary. IMO 手动处理后台线程中的异常并在必要时通过回调重新抛出它们是正确的。

delegate void ExceptionCallback(Exception ex);

void MyExceptionCallback(Exception ex)
{
   throw ex; // Handle/re-throw if necessary
}

void BackgroundThreadProc(Object obj)
{
   try 
   { 
     throw new Exception(); 
   }
   catch (Exception ex)
   { 
     this.BeginInvoke(new ExceptionCallback(MyExceptionCallback), ex); 
   }
}

private void Test()
{
   ThreadPool.QueueUserWorkItem(new WaitCallback(BackgroundThreadProc));
}

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

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