UnobservedTaskException being throw but it is handled by a TaskScheduler.UnobservedTaskException handler and a continuations OnlyOnFaulted handler
The solution was based on How to handle all unhandled exceptions when using Task Parallel Library?
class Program
{
static void Main(string[] args)
{
Program p = new Program();
p.tplTestOne();
}
public void tplTestOne()
{
//-------------------------------------------------
MyClassHere.onUnobservedTaskException += (object sender, EventException e) =>
{
Console.WriteLine(e.Exception.Message); //its fired OK
};
TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs e) =>
{
Console.WriteLine(e.Exception.Message); // its not fired, buggy
};
//-------------------------------------------------
CancellationTokenSource source = new CancellationTokenSource();
Task tz = MyClassHere.CreateHandledTask(
new TaskScheduled(0, () => {
if (!source.IsCancellationRequested)
{
Console.WriteLine("A-main-task-started");
}
Thread.Sleep(5000);
if (source.IsCancellationRequested)
{
Console.WriteLine("CancelingMainTask");
}
})
, new TaskScheduled(3000, () => { Console.WriteLine("okTaskCalled"); })
, null //new TaskScheduled(0, () => { Console.WriteLine("cancelTaskCalled"); })
, TaskCreationOptions.AttachedToParent
, source.Token
, new TaskScheduled(2000, () =>
{
if (!source.IsCancellationRequested)
{
Console.WriteLine("B-timeout");
}
})
, new TaskScheduled(1000, () =>
{
if (!source.IsCancellationRequested)
{
Console.WriteLine("C-timeout");
}
source.Cancel();
})
);
if(tz != null)
{
tz.ContinueWith(t => { Console.WriteLine("END"); });
}
Task tsk_1 = MyClassHere.createHandledTask(() =>
{
double x = 1;
x = (x + 1) / x;
}, false);
Task tsk_2 = MyClassHere.createHandledTask(() =>
{
double y = 0;
throw new Exception("forced_divisionbyzerodontthrowanymore_test"); // here -> System.Exception was unhandled by user code
}, true);
Task tsk_3 = MyClassHere.createHandledTask(() =>
{
double z = 1;
z = (z + 1) / z;
}, true);
Task tsk_4 = MyClassHere.createHandledTask(() =>
{
double k = 1;
k = (k + 1) / k;
}, true);
Console.ReadLine();
}
}
public class EventException : EventArgs
{
public Exception Exception;
public Task task;
public EventException(Exception err, Task tsk)
{
Exception = err;
task = tsk;
}
}
public class TaskScheduled
{
public int waitTime;
public Action action;
public DateTime datestamp;
public bool isCalled = false;
public TaskScheduled(int _waitTime, Action _action)
{
this.waitTime = _waitTime;
this.action = _action;
}
}
public static class MyClassHere
{
public delegate void UnobservedTaskException(object sender, EventException e);
public static event UnobservedTaskException onUnobservedTaskException;
//-------------------------------------------------
public static void waitForTsk(Task t)
{
try
{
t.Wait();
}
catch (AggregateException ae)
{
ae.Handle((err) =>
{
throw err;
});
}
}
//-------------------------------------------------
public static void RaiseUnobsrvEvtForEachIfHappens(this Task task)
{
task.ContinueWith(t =>
{
var aggException = t.Exception.Flatten();
foreach (var exception in aggException.InnerExceptions)
{
onUnobservedTaskException(task, new EventException(exception, task));
}
},
TaskContinuationOptions.OnlyOnFaulted); // not valid for multi task continuations
}
//-------------------------------------------------
public static Task CreateHandledTask(Action action)
{
return CreateHandledTask(action, false);
}
public static Task CreateHandledTask(Action action, bool attachToParent)
{
Task tsk = null;
tsk = CreateHandledTask(action, attachToParent, CancellationToken.None);
return tsk;
}
public static Task CreateHandledTask(Action action, bool attachToParent, CancellationToken cancellationToken)
{
Task tsk = null;
TaskCreationOptions atp = TaskCreationOptions.None;
if (attachToParent) { atp = TaskCreationOptions.AttachedToParent; }
tsk = CreateHandledTask(action, atp, cancellationToken);
return tsk;
}
public static Task CreateHandledTask(Action action, TaskCreationOptions tco, CancellationToken cancellationToken)
{
Task tsk = null;
tsk = Task.Factory.StartNew(action, cancellationToken, tco, TaskScheduler.Default);
tsk.RaiseUnobsrvEvtForEachIfHappens();
return tsk;
}
public static Task CreateHandledTask(TaskScheduled mainTask,
TaskScheduled onSuccessTask,
TaskScheduled onCancelationTask,
TaskCreationOptions tco,
CancellationToken cancellationToken,
params TaskScheduled[] timeouts)
{
Task tsk = null;
ManualResetEvent me = new ManualResetEvent(false);
if (timeouts == null || timeouts.Length < 1 || timeouts[0] == null)
{
tsk = CreateHandledTask(mainTask.action, tco, cancellationToken);
me.Set();
}
else
{
bool isCancelation = false;
bool isSuccess = true;
Task NonBlockCtxTask = CreateHandledTask(() =>
{
tsk = CreateHandledTask(mainTask.action, tco, cancellationToken);
me.Set();
int qtdt = timeouts.Count(st => st.action != null);
CountdownEvent cde_pas = new CountdownEvent(3);
CountdownEvent cde_pat = new CountdownEvent(qtdt);
Parallel.ForEach<TaskScheduled>(timeouts, (ts) =>
{
try
{
bool itsOnTime = tsk.Wait(ts.waitTime, cancellationToken);
cde_pat.Signal();
if (!itsOnTime)
{
isSuccess = false;
Task tact = CreateHandledTask(ts.action, TaskCreationOptions.None, cancellationToken);
}
}
catch (OperationCanceledException oce)
{
isSuccess = false;
cde_pat.Signal(cde_pat.CurrentCount);
isCancelation = true;
}
});
try
{
isSuccess &= cde_pat.Wait(System.Threading.Timeout.Infinite, cancellationToken) && !isCancelation;
}
catch (OperationCanceledException oce)
{
isCancelation = true;
isSuccess = false;
}
finally
{
cde_pas.Signal();
}
try
{
if (isCancelation && onCancelationTask != null)
{
Thread.Sleep(onCancelationTask.waitTime);
Task tcn = CreateHandledTask(onCancelationTask.action);
}
}
catch { }
finally {
cde_pas.Signal();
}
try
{
if (isSuccess && onSuccessTask != null)
{
Thread.Sleep(onSuccessTask.waitTime);
Task tcn = CreateHandledTask(onSuccessTask.action);
}
}
catch { }
finally
{
cde_pas.Signal();
}
cde_pas.Wait(System.Threading.Timeout.Infinite);
}, TaskCreationOptions.None, cancellationToken);
}
me.WaitOne();
return tsk;
}
//-------------------------------------------------
}
TaskScheduler.UnobservedTaskException event handler never being triggered
Unfortunately, that example will never show you your code. The UnobservedTaskException
will only happen if a Task gets collected by the GC with an exception unobserved - as long as you hold a reference to task1
and task2
, the GC will never collect, and you'll never see your exception handler.
In order to see the behavior of the UnobservedTaskException
in action, I'd try the following (contrived example):
public static void Main()
{
TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs eventArgs) =>
{
eventArgs.SetObserved();
((AggregateException)eventArgs.Exception).Handle(ex =>
{
Console.WriteLine("Exception type: {0}", ex.GetType());
return true;
});
};
Task.Factory.StartNew(() =>
{
throw new ArgumentNullException();
});
Task.Factory.StartNew(() =>
{
throw new ArgumentOutOfRangeException();
});
Thread.Sleep(100);
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("Done");
Console.ReadKey();
}
This will show you your messages. The first Thread.Sleep(100)
call provides enough time for the tasks to throw. The collect and wait forces a GC collection, which will fire your event handler 2x.
Exception thrown in Task Thread, not caught by UnobservedTaskException
Here (How to handle exceptions with TaskScheduler.UnobservedTaskException?) is the explanation.
Just replace
Task.WaitAll(tasks.ToArray()); // Can't catch here
with
Task.Factory.StartNew(() =>
{
while (true)
{
Thread.Sleep(1000);
GC.Collect();
}
});
return;
to see the message in TaskScheduler_UnobservedTaskException
Handling unobserved Task exceptions
Per app-domain is all. It's a static event off the Task
class which you should just make sure you hook at logical application startup time. So for a console app, just hook it in Main before you start any other work. For ASP.NET apps, just hook it in Global.asax Application_Start. For a Windows service, in OnStart.
How to handle all unhandled exceptions when using Task Parallel Library?
Seems like there's no built-in way to handle this (and no answer to this question after almost 2 weeks). I already rolled out some custom code to take care of this. The solution description is pretty lengthy, so I've posted in my blog. Refer to this post if you're interested.
Update 5/7/2010: I’ve found a better way to do that, making use of task continuation. I create a class ThreadFactory
that exposes the Error event which can be subscribed by a top-level handler and provides methods to start a task attached with proper continuation.
The code is posted here.
Update 4/18/2011: Post code from the blog post as per Nifle's comment.
internal class ThreadFactory
{
public delegate void TaskError(Task task, Exception error);
public static readonly ThreadFactory Instance = new ThreadFactory();
private ThreadFactory() {}
public event TaskError Error;
public void InvokeError(Task task, Exception error)
{
TaskError handler = Error;
if (handler != null) handler(task, error);
}
public void Start(Action action)
{
var task = new Task(action);
Start(task);
}
public void Start(Action action, TaskCreationOptions options)
{
var task = new Task(action, options);
Start(task);
}
private void Start(Task task)
{
task.ContinueWith(t => InvokeError(t, t.Exception.InnerException),
TaskContinuationOptions.OnlyOnFaulted |
TaskContinuationOptions.ExecuteSynchronously);
task.Start();
}
}
is it possible to catch when any Task terminates due exception and log?
For tasks that you create yourself, it's reasonably simple: create your own methods which call Task.Factory.StartNew()
, but then also call Task.ContinueWith(loggingDelegate, TaskContinuationOptions.OnlyOnFaulted
before returning the task.
The problem is that that won't add a fault handler for tasks created by other bits of infrastructure - including by async methods in C# 5. It still might be useful to you though.
You can also use TaskScheduler.UnobservedTaskException
, but as per the name that will only be called for exceptions which aren't already observed by something else. (Again, that may be fine for you...)
Related Topics
Using Dataannotations with Entity Framework
How to Use Default Serialization in a Custom System.Text.JSON JSONconverter
Is There Any Valid Reason to Ever Ignore a Caught Exception
How to Take a Screenshot of a Wpf Control
Specifying Generic Collection Type Param at Runtime
Sqldatasourceenumerator.Instance.Getdatasources() Does Not Locate Local SQL Server 2008 Instance
.Net and Bitmap Not Automatically Disposed by Gc When There Is No Memory Left
How Does Task<Int> Become an Int
How to Add a Line Break in C# .Net Documentation
Display Progress Bar While Doing Some Work in C#
Suppress Properties with Null Value on ASP.NET Web API
Only Parameterless Constructors and Initializers Are Supported in Linq to Entities
Unzip a Memorystream (Containing the Zip File) and Get the Files