多线程应用程序中的异常。

时间:2011-04-06 16:08:14

标签: c# .net multithreading exception

我从一个非常挑剔的人那里听说,一个线程中抛出(而不是被捕获)的异常正在传播到父线程。真的吗? 我尝试过这样的东西,但是在创建线程中无法捕获异常。

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

9 个答案:

答案 0 :(得分:12)

线程的异常不会传播到主线程的上下文。这确实有意义 - 在抛出异常时,主线程通常与包含异常处理程序的范围完全不同。

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

答案 1 :(得分:2)

答案 2 :(得分:1)

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

答案 3 :(得分:1)

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

答案 4 :(得分:1)

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

如果从某个方法启动一个新线程,它将向上传播,直到达到该方法。

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

答案 5 :(得分:1)

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

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

答案 6 :(得分:1)

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

  1. 在新线程启动的方法(在创建的线程的堆栈顶部)中将其注销。
  2. 使用像异步委托这样的构造,当你调用end invoke时会返回异常,然后你就可以正常方式捕获它。

答案 7 :(得分:1)

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

没有“父”线程的概念。

答案 8 :(得分:0)

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