【问题标题】: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 窗体应用程序的差异等,请参见该页面。
【解决方案3】:
不,不会。为了捕获线程异常,您需要使用 Application.ThreadException。
【解决方案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】:
简而言之,不,它没有。你有几个明显的选择:#
- 在新线程启动的方法(已创建线程的堆栈顶部)中将其注销。
- 使用异步委托之类的构造,当您调用结束调用时将返回异常,然后您可以以正常方式捕获。
【解决方案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);
}
}