catch exception that is thrown in different thread
One of my method (Method1
) spawns a new thread.
That thread execute a method (Method2
) and during exectution an exception is thrown.
I need to get that exception information on t开发者_运维知识库he calling method (Method1
)
Is there someway I can catch this exception in Method1
that is thrown in Method2
?
In .NET 4 and above, you can use Task<T>
class instead of creating new thread. Then you can get exceptions using .Exceptions
property on your task object.
There are 2 ways to do it:
In a separate method: // You process exception in some task's thread
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); } }
In the same method: // You process exception in the caller's thread
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(); } }
Note that the exception which you get is AggregateException
. All real exceptions are availible through ex.InnerExceptions
property.
In .NET 3.5 you can use the following code:
// You process exception in the child's thread
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(); } }
Or // You process exception in the caller's thread
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(); } }
You can not catch the exception in Method1. You can, however, catch the exception in Method2 and record it to a variable that the original thread of execution can then read and work with.
I had a particular problem in that I wanted to use items, containing controls, from an integration test suite, so have to create an STA thread. The code I ended up with is as follows, put here in case others have the same issue.
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) { ... }
This is a direct paste of the code as-is. For other uses I would recommend supplying an action or function as a parameter and invoking that on the thread instead of hard-coding the called method.
The simplest method to share data between different threads is shared data
as follows (some is pseudo code):
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;
}
}
You can read about this method in this nice introduction about multithreading, however, I preferred to read about this in the O'Reilly book C# 3.0 in a nutshell
, by the brothers Albahari (2007), which is also freely accessible on Google Books, just like the newer version of the book, because it also covers thread pooling, foreground versus background threads, etc etc, with nice and simple example code. (Disclaimer: I own a worn-out copy of this book)
In case you are making a WinForms application, the use of shared data is especially handy, because the WinForm controls are not thread-safe. Using a callback to pass data from the worker thread back to a WinForm control the main UI thread needs ugly code with Invoke()
to make that control thread-safe. Using shared data instead, and the single-threaded System.Windows.Forms.Timer
, with a short Interval
of say 0.2 seconds, you can easily send information from the worker thread to the control without Invoke
.
Here is the code that I used to throw the exception back to the main thread to be caught.
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;
}
}
精彩评论