Исключения в многопоточном приложении. - PullRequest
7 голосов
/ 06 апреля 2011

Я слышал от очень проницательного человека, что исключение, которое выдается (и не перехватывается) в потоке, распространяется в родительский поток. Это правда? Я пробовал что-то подобное, но не смог поймать исключение в потоке создания.

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

Ответы [ 10 ]

12 голосов
/ 06 апреля 2011

Исключение потока не будет распространяться на контекст основного потока.Это действительно имеет смысл - к моменту создания исключения основной поток, как правило, будет находиться в совершенно иной области видимости, чем та, в которой содержится ваш обработчик исключений.

Вы можете перехватить эти исключения (обычно для их регистрации) с помощьюподключение к AppDomain.UnhandledException .Для получения подробной информации см. Эту страницу, в том числе различия в приложениях Windows Forms и т. Д.

2 голосов
/ 06 апреля 2011

Это отличная статья о потоках в C # и о том, как обрабатывать исключения

1 голос
/ 12 мая 2017

Существует очень простое решение для особого случая, когда основной поток ожидает завершения другого потока (см. 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();
    }
}
1 голос
/ 06 апреля 2011

Исключение можно поймать только в потоке, из которого пришло. Таким образом, создание исключения в другом потоке не приведет к его перехвату другим потоком.

Нет понятия "родительский" поток.

1 голос
/ 06 апреля 2011

Короче, нет, это не так. У вас есть несколько очевидных вариантов: #

  1. Выйдите из него методом, с которого начинается новый поток (верх стека для созданного потока).
  2. Используйте конструкцию, подобную асинхронному делегату, который будет возвращать исключение при вызове end invoke, который затем можно перехватить обычным способом.
1 голос
/ 06 апреля 2011

Вот один из способов поймать его и безопасно обращаться с ним:

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);
    }
};
1 голос
/ 06 апреля 2011

Исключение распространяется вверх в стеке вызовов.

Если вы начинаете новый поток с определенного метода, он будет распространяться вверх, пока не достигнет этого метода.

Если этот метод не перехватывает его, вы получите ошибку времени выполнения, что есть необработанное исключение.

1 голос
/ 06 апреля 2011

Достойный ресурс на Threading в C #, который имеет раздел для обработки исключений: Threading в C #

1 голос
/ 06 апреля 2011

Нет, не будет.Чтобы перехватывать исключения потоков, вам нужно использовать Application.ThreadException.

0 голосов
/ 08 апреля 2019
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);
   }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...