【问题标题】:Exceptions in multithreaded application.多线程应用程序中的异常。
【发布时间】:2011-07-30 23:41:06
【问题描述】:

我从一个非常有眼光的人那里听说,线程中抛出(而不是捕获)的异常正在传播到父线程。真的吗? 我尝试过类似的方法,但无法在创建线程中捕获异常。

    static void Main(string[] args)
    {
        ParameterizedThreadStart pts = 
           new ParameterizedThreadStart(ThreadMethod);
        try
        {
            Thread t = new Thread(pts);
            t.Start(new object());
            Console.ReadLine();
        }
        catch (Exception ex) //the exception is not caught
        {
            Debugger.Break();
        }
    }


    static void ThreadMethod(object @object)
    {
        Thread.Sleep(2000);
        throw new IndexOutOfRangeException();
        Thread.CurrentThread.Abort();
    }

【问题讨论】:

标签: c# .net multithreading exception


【解决方案1】:

线程的异常不会传播到主线程的上下文。这确实是有道理的——当异常被抛出时,主线程通常与包含你的异常处理程序的范围完全不同。

您可以通过连接到AppDomain.UnhandledException 来捕获这些异常(通常用于记录它们)。有关详细信息,包括 Windows 窗体应用程序的差异等,请参见该页面。

【讨论】:

    【解决方案2】:

    【讨论】:

      【解决方案3】:

      不,不会。为了捕获线程异常,您需要使用 Application.ThreadException。

      【讨论】:

        【解决方案4】:

        一个不错的 C# 线程资源,其中包含异常处理部分:Threading in C#

        【讨论】:

          【解决方案5】:

          异常在调用堆栈中向上传播。

          如果你从某个方法开始一个新线程,它会向上传播,直到到达那个方法。

          如果该方法没有捕获它,您将收到一个运行时错误,表明存在未捕获的异常。

          【讨论】:

            【解决方案6】:

            以下是一种捕获并以安全方式处理它的方法:

            BackgroundWorker bg = new BackgroundWorker();
            object o;
            bg.DoWork += (c1,c2) =>
            {         
                    Thread.Sleep(2000);        
                    throw new IndexOutOfRangeException();
            };
            
            bg.RunWorkerCompleted += (c3,c4) => 
            {
            
             if (((RunWorkerCompletedEventArgs)e).Error != null)
                {
                    MessageBox.Show(((RunWorkerCompletedEventArgs)e).Error.Message);
                }
            };
            

            【讨论】:

              【解决方案7】:

              简而言之,不,它没有。你有几个明显的选择:#

              1. 在新线程启动的方法(已创建线程的堆栈顶部)中将其注销。
              2. 使用异步委托之类的构造,当您调用结束调用时将返回异常,然后您可以以正常方式捕获。

              【讨论】:

                【解决方案8】:

                只能在来自的线程上捕获异常。所以在另一个线程上抛出异常不会导致另一个线程捕获它。

                没有“父”线程的概念。

                【讨论】:

                  【解决方案9】:

                  当主线程等待另一个线程完成时,有一个非常简单的解决方案(参见https://msdn.microsoft.com/en-us/library/58195swd(v=vs.110).aspx#Examples)。它只需要一个类异常变量。

                  由异常处理程序完成的修改后的代码示例可能如下所示。

                  using System;
                  using System.Threading;
                  
                  class WaitOne
                  {
                      static AutoResetEvent autoEvent = new AutoResetEvent(false);
                      static Exception SavedException = null;
                  
                      static void Main()
                      {
                          Console.WriteLine("Main starting.");
                  
                          ThreadPool.QueueUserWorkItem(
                              new WaitCallback(WorkMethod), autoEvent);
                  
                          // Wait for work method to signal.
                          autoEvent.WaitOne();
                          if (SavedException != null)
                          {
                             // handle this exception as you want
                          }
                  
                          Console.WriteLine("Work method signaled.\nMain ending.");
                      }
                  
                      static void WorkMethod(object stateInfo) 
                      {
                          Console.WriteLine("Work starting.");
                  
                          // Simulate time spent working.
                          try
                          {
                            Thread.Sleep(new Random().Next(100, 2000));
                            throw new Exception("Test exception");
                          }
                          catch (Exception ex)
                          {
                              SavedException = ex;
                          }            
                  
                          // Signal that work is finished.
                          Console.WriteLine("Work ending.");
                          ((AutoResetEvent)stateInfo).Set();
                      }
                  }
                  

                  【讨论】:

                    【解决方案10】:
                    We can use CustomExceptionHandler for this. Below code might help you.
                    
                        using System;
                        using System.Threading;
                        using System.Windows.Forms;
                    
                        // Create a form with a button that, when clicked, throws an exception.
                         public class ErrorForm : System.Windows`enter code here`.Forms.Form
                         {
                            internal Button button1;
                    
                            public ErrorForm() : base()
                            {
                               // Add the button to the form.
                               this.button1 = new System.Windows.Forms.Button();
                               this.SuspendLayout();
                               this.button1.Location = new System.Drawing.Point(100, 43);
                               this.button1.Size = new System.Drawing.Size(75, 23);
                               this.button1.Text = "Click!";
                               this.Controls.Add(this.button1);
                               this.button1.Click += new EventHandler(this.button1_Click);
                    
                               this.Text = "ThreadException";
                               this.ResumeLayout(false);
                            }
                    
                            // Throw an exception when the button is clicked.
                            private void button1_Click(object sender, System.EventArgs e)
                            {
                               throw new ArgumentException("The parameter was invalid");
                            }
                    
                            public static void Main()
                            {
                               // Add the event handler.
                               Application.ThreadException += new ThreadExceptionEventHandler(CustomExceptionHandler.OnThreadException);
                    
                               // Start the example.
                               Application.Run(new ErrorForm());
                            }
                         }
                    
                         // Create a class to handle the exception event.
                         internal class CustomExceptionHandler
                         {
                             // Handle the exception event
                            public static void OnThreadException(object sender, ThreadExceptionEventArgs t)
                            {
                               DialogResult result = ShowThreadExceptionDialog(t.Exception);
                    
                               // Exit the program when the user clicks Abort.
                               if (result == DialogResult.Abort) 
                                  Application.Exit();
                            }
                    
                            // Create and display the error message.
                            private static DialogResult ShowThreadExceptionDialog(Exception e)
                            {
                               string errorMsg = "An error occurred.  Please contact the adminstrator " +
                                    "with the following information:\n\n";
                               errorMsg += String.Format("Exception Type: {0}\n\n", e.GetType().Name);
                               errorMsg += "\n\nStack Trace:\n" + e.StackTrace;
                               return MessageBox.Show(errorMsg, "Application Error", 
                                    MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Stop);
                            }
                         }
                    
                    2nd approach:-
                    
                    using System;
                    using System.IO;
                    using System.Threading.Tasks;
                    
                    class Example
                    {
                       static async Task Main(string[] args)
                       {
                          // Get a folder path whose directories should throw an UnauthorizedAccessException.
                          string path = Directory.GetParent(
                                                  Environment.GetFolderPath(
                                                  Environment.SpecialFolder.UserProfile)).FullName;
                    
                          // Use this line to throw UnauthorizedAccessException, which we handle.
                          Task<string[]> task1 = Task<string[]>.Factory.StartNew(() => GetAllFiles(path));
                    
                          // Use this line to throw an exception that is not handled.
                          // Task task1 = Task.Factory.StartNew(() => { throw new IndexOutOfRangeException(); } );
                          try {
                              await task1;
                          }
                          catch (AggregateException ae) {
                              ae.Handle((x) =>
                              {
                                  if (x is UnauthorizedAccessException) // This we know how to handle.
                                  {
                                      Console.WriteLine("You do not have permission to access all folders in this path.");
                                      Console.WriteLine("See your network administrator or try another path.");
                                      return true;
                                  }
                                  return false; // Let anything else stop the application.
                              });
                          }
                    
                          Console.WriteLine("task1 Status: {0}{1}", task1.IsCompleted ? "Completed," : "", 
                                                                    task1.Status);
                       }
                    
                       static string[] GetAllFiles(string str)
                       {
                          // Should throw an UnauthorizedAccessException exception.
                          return System.IO.Directory.GetFiles(str, "*.txt", System.IO.SearchOption.AllDirectories);
                       }
                    }
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2021-01-28
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2013-08-06
                      • 2020-04-30
                      相关资源
                      最近更新 更多