C# 捕获在不同线程中引发的异常
我的一个方法(C# 捕获在不同线程中引发的异常,c#,multithreading,exception-handling,C#,Multithreading,Exception Handling,我的一个方法(Method1)生成一个新线程。 该线程执行一个方法(Method2),在执行过程中引发异常。 我需要获取调用方法(Method1)的异常信息 是否有某种方法可以在Method1中捕获在Method2中抛出的异常?您无法在Method1中捕获异常。但是,您可以在Method2中捕获异常并将其记录到一个变量中,然后原始执行线程可以读取并使用该变量。在.NET 4及更高版本中,您可以使用任务类而不是创建新线程。然后可以使用任务对象上的.exceptions属性获取异常。 有两种方法可以
Method1
)生成一个新线程。
该线程执行一个方法(Method2
),在执行过程中引发异常。
我需要获取调用方法(Method1
)的异常信息
是否有某种方法可以在
Method1
中捕获在Method2
中抛出的异常?您无法在Method1中捕获异常。但是,您可以在Method2中捕获异常并将其记录到一个变量中,然后原始执行线程可以读取并使用该变量。在.NET 4及更高版本中,您可以使用任务
类而不是创建新线程。然后可以使用任务对象上的.exceptions
属性获取异常。
有两种方法可以做到这一点:
class Program
{
static void Main(string[] args)
{
Task<int> task = new Task<int>(Test);
task.ContinueWith(ExceptionHandler, TaskContinuationOptions.OnlyOnFaulted);
task.Start();
Console.ReadLine();
}
static int Test()
{
throw new Exception();
}
static void ExceptionHandler(Task<int> task)
{
var exception = task.Exception;
Console.WriteLine(exception);
}
}
class Program
{
static void Main(string[] args)
{
Task<int> task = new Task<int>(Test);
task.Start();
try
{
task.Wait();
}
catch (AggregateException ex)
{
Console.WriteLine(ex);
}
Console.ReadLine();
}
static int Test()
{
throw new Exception();
}
}
class Program
{
static void Main(string[] args)
{
Exception exception = null;
Thread thread = new Thread(() => SafeExecute(() => Test(0, 0), Handler));
thread.Start();
Console.ReadLine();
}
private static void Handler(Exception exception)
{
Console.WriteLine(exception);
}
private static void SafeExecute(Action test, Action<Exception> handler)
{
try
{
test.Invoke();
}
catch (Exception ex)
{
Handler(ex);
}
}
static void Test(int a, int b)
{
throw new Exception();
}
}
class Program
{
static void Main(string[] args)
{
Exception exception = null;
Thread thread = new Thread(() => SafeExecute(() => Test(0, 0), out exception));
thread.Start();
thread.Join();
Console.WriteLine(exception);
Console.ReadLine();
}
private static void SafeExecute(Action test, out Exception exception)
{
exception = null;
try
{
test.Invoke();
}
catch (Exception ex)
{
exception = ex;
}
}
static void Test(int a, int b)
{
throw new Exception();
}
}
class Program
{
static void Main(string[] args)
{
Task<int> task = new Task<int>(Test);
task.ContinueWith(ExceptionHandler, TaskContinuationOptions.OnlyOnFaulted);
task.Start();
Console.ReadLine();
}
static int Test()
{
throw new Exception();
}
static void ExceptionHandler(Task<int> task)
{
var exception = task.Exception;
Console.WriteLine(exception);
}
}
class Program
{
static void Main(string[] args)
{
Task<int> task = new Task<int>(Test);
task.Start();
try
{
task.Wait();
}
catch (AggregateException ex)
{
Console.WriteLine(ex);
}
Console.ReadLine();
}
static int Test()
{
throw new Exception();
}
}
class Program
{
static void Main(string[] args)
{
Exception exception = null;
Thread thread = new Thread(() => SafeExecute(() => Test(0, 0), Handler));
thread.Start();
Console.ReadLine();
}
private static void Handler(Exception exception)
{
Console.WriteLine(exception);
}
private static void SafeExecute(Action test, Action<Exception> handler)
{
try
{
test.Invoke();
}
catch (Exception ex)
{
Handler(ex);
}
}
static void Test(int a, int b)
{
throw new Exception();
}
}
class Program
{
static void Main(string[] args)
{
Exception exception = null;
Thread thread = new Thread(() => SafeExecute(() => Test(0, 0), out exception));
thread.Start();
thread.Join();
Console.WriteLine(exception);
Console.ReadLine();
}
private static void SafeExecute(Action test, out Exception exception)
{
exception = null;
try
{
test.Invoke();
}
catch (Exception ex)
{
exception = ex;
}
}
static void Test(int a, int b)
{
throw new Exception();
}
}
在不同线程之间共享数据的最简单方法是
共享数据
,如下所示(有些是伪代码):
你可以在中读到这个方法,但是,我更喜欢在Albahari兄弟(2007)的《O'Reilly C#3.0简而言之》(C#3.0)一书中读到这个方法,这本书也可以在Google Books上免费访问,就像这本书的更新版本一样,因为它还包括线程池、前台线程与后台线程等,使用漂亮简单的示例代码。(免责声明:我拥有本书的一份旧版)
如果您正在制作WinForms应用程序,使用共享数据尤其方便,因为WinForm控件不是线程安全的。使用回调将数据从工作线程传递回WinForm控件,主UI线程需要使用
Invoke()
编写丑陋的代码,以使该控件线程安全。相反,使用共享数据和单线程System.Windows.Forms.Timer
,间隔时间短,比如说0.2秒,您可以轻松地将信息从工作线程发送到控件,而无需调用Invoke我有一个特别的问题,我想使用包含控件的项,来自集成测试套件,因此必须创建STA线程。我最后得到的代码如下,放在这里,以防其他人也有同样的问题
public Boolean? Dance(String name) {
// Already on an STA thread, so just go for it
if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) return DanceSTA(name);
// Local variable to hold the caught exception until the caller can rethrow
Exception lException = null;
Boolean? lResult = null;
// A gate to hold the calling thread until the called thread is done
var lGate = new ManualResetEvent(false);
var lThreadStart = new ThreadStart(() => {
try {
lResult = DanceSTA(name);
} catch (Exception ex) {
lException = ex;
}
lGate.Set();
});
var lThread = new Thread(lThreadStart);
lThread.SetApartmentState(ApartmentState.STA);
lThread.Start();
lGate.WaitOne();
if (lException != null) throw lException;
return lResult;
}
public Boolean? DanceSTA(String name) { ... }
这是代码的直接粘贴。对于其他用途,我建议提供一个动作或函数作为参数,并在线程上调用它,而不是硬编码被调用的方法。感谢您的回复。所以如果Method1是Class1的一部分,并且我在该类中有一个Exception类型的变量。每当Method2抛出异常时,它也会在Class1中设置该异常变量。这听起来像是一个公平的设计吗?是否有处理此场景的最佳实践方法?正确,您只需存储异常并稍后访问它。将来运行的方法(尤其是Method2完成时的回调)会重新引发该异常,就好像它们自己导致了该异常一样,这并不少见,但这取决于您想要什么。很抱歉,我忘了提到我使用的是.NET 3.5。根据我的理解,任务是4.0吗?@SilverlightStudent好的,我刚刚更新了我的答案以满足您的要求。@oxilumin:谢谢,非常感谢。还有一个后续问题。如果您的Test()方法也包含一些参数,那么您将如何修改这些参数的SafeExecute方法?@SilverlightStudent在这种情况下,我将传递lambda而不是Test
。像()=>测试(myParameter1,myParameter2)
@SilverlightStudent:Updated。