我的一个方法 (Method1
) 开启了一个新线程。
该线程执行一个方法 (Method2
),在执行过程中抛出异常。
我需要在调用方法 (Method1
) 中获取该异常信息。
有没有办法在 Method2
中抛出的异常被 Method1
捕获呢?
我的一个方法 (Method1
) 开启了一个新线程。
该线程执行一个方法 (Method2
),在执行过程中抛出异常。
我需要在调用方法 (Method1
) 中获取该异常信息。
有没有办法在 Method2
中抛出的异常被 Method1
捕获呢?
在.NET 4及以上版本中,您可以使用Task<T>
类来替换创建新线程。然后,您可以使用任务对象上的.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
属性获得。//您可以在子线程中处理异常
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();
}
}
Method1 中无法捕获异常。但是在 Method2 中可以捕获异常并记录到一个变量中,原始执行线程可以读取并处理该变量。
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) { ... }
共享数据
,示例如下(部分伪代码):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;
}
}
你可以在这篇关于多线程的好文章中了解到这种方法,但是我更喜欢在阅读《C# 3.0 in a nutshell》这本由Albahari兄弟(2007)编写的O'Reilly书籍中了解它。这本书也可以在Google Books上免费获取,因为它还涵盖了线程池、前台与后台线程等内容,并提供了简单易懂的示例代码。(免责声明:我拥有一本破旧的这本书)
如果你正在制作一个WinForms应用程序,使用共享数据尤其方便,因为WinForm控件不是线程安全的。使用回调将数据从工作线程传递回主UI线程的WinForm控件需要使用Invoke()
编写丑陋的代码来使该控件线程安全。相反,使用共享数据和单线程的System.Windows.Forms.Timer
,并设置短的Interval
,例如0.2秒,你可以轻松地将信息从工作线程发送到控件而无需使用Invoke
。
这是我用来将异常抛回到主线程以便捕获的代码。
class Program
{
static void Main(string[] args)
{
CancellationTokenSource cancelToken = new CancellationTokenSource();
Exception taskException = null;
var timerTask = Task.Factory.StartNew(() =>
{
for (;;)
{
if (cancelToken.IsCancellationRequested)
break;
ContinuousTask();
Thread.Sleep(400);
}
}, cancelToken.Token).ContinueWith((t, o) => {
taskException = t.Exception;
((Thread)o).Interrupt();
}, Thread.CurrentThread, TaskContinuationOptions.OnlyOnFaulted);
try
{
//do a bunch of tasks here
//want to skip the do while and go to the catch if exception is thrown
do
{
System.Threading.Thread.Sleep(200);
} while (true);
}
catch
{
if (taskException != null)
Console.WriteLine(taskException.Message);
}
}
private static int _loopCounter = 0;
public static void ContinuousTask()
{
int counter = 0;
do
{
if (_loopCounter >= 3)
throw new Exception("error");
if (counter >= 5)
break;
counter += 1;
System.Threading.Thread.Sleep(100);
} while (true);
_loopCounter += 1;
}
}
Test
。就像() => Test(myParameter1, myParameter2)
这样。 - oxilumin