我的一个方法(Method1
)产生了一个新线程。该线程执行方法(Method2
),并且在执行期间引发异常。我需要获取有关调用方法(Method1
)的异常信息
是有什么方法我能赶上这个例外Method1
是在抛出Method2
?
我的一个方法(Method1
)产生了一个新线程。该线程执行方法(Method2
),并且在执行期间引发异常。我需要获取有关调用方法(Method1
)的异常信息
是有什么方法我能赶上这个例外Method1
是在抛出Method2
?
Answers:
在.NET 4及更高版本中,您可以使用Task<T>
class而不是创建新线程。然后,您可以使用.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();
}
}
请注意,您得到的异常是AggregateException
。所有实际的异常都可以通过ex.InnerExceptions
属性获得。
在.NET 3.5中,您可以使用以下代码:
//您在子线程中处理异常
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();
}
}
Test
。像() => Test(myParameter1, myParameter2)
您不能在Method1中捕获异常。但是,您可以在Method2中捕获该异常并将其记录到一个变量中,原始执行线程随后可以读取和使用该变量。
在不同线程之间共享数据的最简单方法shared data
如下(有些是伪代码):
class MyThread
{
public string SharedData;
public void Worker()
{
...lengthy action, infinite loop, etc...
SharedData = "whatever";
...lengthy action...
return;
}
}
class Program
{
static void Main()
{
MyThread m = new MyThread();
Thread WorkerThread = new Thread(m.Worker);
WorkerThread.Start();
loop//or e.g. a Timer thread
{
f(m.SharedData);
}
return;
}
}
您可以在有关多线程的漂亮介绍中阅读此方法,但是,我更喜欢在O'Reilly book C# 3.0 in a nutshell
Albahari兄弟(2007)的上阅读有关此方法的信息,该书也可以在Google图书中免费获得,就像该书的新版本一样,因为它还通过漂亮而简单的示例代码介绍了线程池,前台线程与后台线程等。(免责声明:我拥有这本书的破旧副本)
如果要制作WinForms应用程序,则共享数据的使用特别方便,因为WinForm控件不是线程安全的。使用回调将数据从辅助线程传递回WinForm控件时,主UI线程需要使用难看的代码Invoke()
才能使该控件成为线程安全的。System.Windows.Forms.Timer
只需使用共享数据和单线程,只需短短Interval
的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) { ... }
这是按原样直接粘贴代码的。对于其他用途,我建议提供一个动作或函数作为参数,并在线程上调用它,而不是硬编码所调用的方法。