我有一个对象A,我想用对象的B方法执行新的线程。我可以使用Task.CreateNew等。问题是我不知道如何处理新线程中的异常。
一般来说,我想要的是内部线程与对象的B方法抛出异常,父对象A将捕获并关闭它的执行,以及对象B.
我无法将代码添加到主循环中
主循环完成后捕获异常是不可接受的,我想要 按时发现内部线程异常
有没有办法实现它?
在下面的代码中,我没有异常处理和主线程继续:
static void Main()
{
Console.WriteLine("start");
Task.Factory.StartNew(PrintTime, CancellationToken.None);
for (int i = 0; i < 20; i++)
{
Console.WriteLine("master thread i={0}", i + 1);
Thread.Sleep(1000);
}
}
private static void PrintTime()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("inner thread i={0}",i+1);
Thread.Sleep(1000);
}
throw new Exception("exception");
}
答案 0 :(得分:2)
保留对任务实例的引用,并在准备好处理其结果时在其上调用Wait
。在任务执行期间抛出的任何未处理的内部异常都将包含在AggregateException
中,而{{3}}将从Wait
方法抛出。
static void Main()
{
Console.WriteLine("start");
Task task = Task.Factory.StartNew(PrintTime, CancellationToken.None);
for (int i = 0; i < 20; i++)
{
Console.WriteLine("master thread i={0}", i + 1);
Thread.Sleep(1000);
// Stop iterating in case of unhandled exception in inner task.
if (task.Status == TaskStatus.Faulted)
break;
}
try
{
task.Wait();
}
catch (AggregateException ae)
{
ae.Handle((x) =>
{
Console.WriteLine("Exception: " + x.ToString());
});
}
}
答案 1 :(得分:1)
无论哪个线程最初抛出异常,以下解决方案都有效:
static void Main()
{
Console.WriteLine("start");
var innerCts = new CancellationTokenSource();
Exception mainException = null;
var mainThread = new Thread(() => SafeMainThread(innerCts, ref mainException));
mainThread.Start();
var innerTask = Task.Factory.StartNew(state => PrintTime(state),
innerCts,
innerCts.Token,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
var innerFault = innerTask.ContinueWith(t => { Console.WriteLine("Inner thread caused " + t.Exception.InnerException.GetType().Name + ". Main thread is being aborted..."); mainThread.Abort(); },
TaskContinuationOptions.OnlyOnFaulted);
var innerCancelled = innerTask.ContinueWith(_ => Console.WriteLine("Inner thread cancelled."),
TaskContinuationOptions.OnlyOnCanceled);
var innerSucceed = innerTask.ContinueWith(_ => Console.WriteLine("Inner thread completed."),
TaskContinuationOptions.OnlyOnRanToCompletion);
try
{
innerTask.Wait();
}
catch (AggregateException)
{
// Ignore.
}
mainThread.Join();
Console.ReadLine();
}
private static void SafeMainThread(CancellationTokenSource innerCts, ref Exception mainException)
{
try
{
MainThread();
Console.WriteLine("Main thread completed.");
}
catch (ThreadAbortException)
{
Console.WriteLine("Main thread aborted.");
}
catch (Exception exception)
{
Console.WriteLine("Main thread caused " + exception.GetType().Name + ". Inner task is being canceled...");
innerCts.Cancel();
mainException = exception;
}
}
private static void MainThread()
{
for (int i = 0; i < 20; i++)
{
Console.WriteLine("master thread i={0}", i + 1);
Thread.Sleep(500);
}
throw new Exception("exception");
}
private static void PrintTime(object state)
{
var cts = (CancellationTokenSource)state;
for (int i = 0; i < 10; i++)
{
cts.Token.ThrowIfCancellationRequested();
Console.WriteLine("inner thread i={0}", i + 1);
Thread.Sleep(500);
}
throw new Exception("exception");
}
答案 2 :(得分:0)
尝试拦截内部异常并在主循环中检查其值,就好像是取消请求一样:
static void Main()
{
Console.WriteLine("start");
try
{
AggregateException innerException = null;
Task.Factory.StartNew(PrintTime, CancellationToken.None)
.ContinueWith(t => innerException = t.Exception, TaskContinuationOptions.OnlyOnFaulted);
for (int i = 0; i < 20; i++)
{
if (innerException != null)
throw innerException;
Console.WriteLine("master thread i={0}", i + 1);
Thread.Sleep(1000);
}
}
catch (AggregateException)
{
Console.WriteLine("Inner thread caused exception. Main thread handles that exception.");
}
}
答案 3 :(得分:0)
您可以在发生异常时尝试运行异常处理程序。你可以使用一个标志,我在例子中使用异常来检查异常发生本身:
private static AggregateException exception = null;
static void Main(string[] args)
{
Console.WriteLine("Start");
Task.Factory.StartNew(PrintTime, CancellationToken.None).ContinueWith(HandleException, TaskContinuationOptions.OnlyOnFaulted);
for (int i = 0; i < 20; i++)
{
Console.WriteLine("Master Thread i={0}", i + 1);
Thread.Sleep(1000);
if (exception != null)
{
break;
}
}
Console.WriteLine("Finish");
Console.ReadLine();
}
private static void HandleException(Task task)
{
exception = task.Exception;
Console.WriteLine(exception);
}
private static void PrintTime()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Inner Thread i={0}", i + 1);
Thread.Sleep(1000);
}
throw new Exception("exception");
}
答案 4 :(得分:0)
如果你不能修改主体的内部,我不知道如何对它进行细粒度的控制。我现在看到的解决方案是将主体包装到托管线程中,如果内部线程抛出异常,则允许中止它:
static void Main()
{
Console.WriteLine("start");
var mainThread = new Thread(MainThread);
mainThread.Start();
var task = Task.Factory
.StartNew(PrintTime)
.ContinueWith(t => { Console.WriteLine("Inner thread caused exception. Main thread is being aborted."); mainThread.Abort(); },
TaskContinuationOptions.OnlyOnFaulted);
task.Wait();
Console.WriteLine("Waiting for main thread to abort...");
mainThread.Join();
Console.WriteLine("Main thread aborted.");
Console.ReadLine();
}
private static void MainThread()
{
for (int i = 0; i < 20; i++)
{
Console.WriteLine("master thread i={0}", i + 1);
Thread.Sleep(1000);
}
}
private static void PrintTime()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("inner thread i={0}", i + 1);
Thread.Sleep(1000);
}
throw new Exception("exception");
}