When you throw an exception in a task execution, for example:
Task. Factory. startnew () =>
{
Throw new exception ();
});
Run this method and no exception is thrown.
In fact, at this time, the task exception is in the imperceptible state. This imperceptible state exception will be thrown in the terminator execution thread during garbage collection.
To induce this exception, we can use GC. Collect to force garbage collection to cause the terminator to process the thread. At this time, the imperceptible exception of the task will be thrown.
// Throw an exception in the task
Task. Factory. startnew () =>
{
Throw new exception ();
});
// Make sure the task is completed
Thread. Sleep (100 );
// Force the spam to receive
GC. Collect ();
// Wait for the terminator to process
GC. waitforpendingfinalizers ();
OK, an exception is thrown, the program crashes, and the output is as follows:
Unhandled exception: system. aggresponexception: a task's exception (s) were not
Bserved either by waiting on the task or accessing its exception property. As
Result, the unobserved exception was rethrown by the finalizer thread. ---> sys
Em. Exception: exception of Type 'System. exception' was thrown.
At mgen. program. <main> B _ 0 () in E: \ Users \ mgen \ Documents \ Visual Studio 2010 \ P
Ojects \ mgen \ Program. CS: Line 19
At system. Threading. Tasks. task. innerinvoke ()
At system. Threading. Tasks. task. Execute ()
--- End of inner exception stack trace ---
At system. Threading. Tasks. taskexceptionholder. Finalize ()
You can detect exceptions of a task by calling task. Wait/waitall, referencing the task <t>. Result attribute, or referencing the task. Exception attribute in the simplest way. For example:
Manually capture aggresponexception through task. Wait:
Try
{
Task. waitall (
Task. Factory. startnew () =>
{
Throw new exception ();
}));
}
Catch (aggresponexception)
{}
// Make sure the task is completed
Thread. Sleep (100 );
// Force the spam to receive
GC. Collect ();
// Wait for the terminator to process
GC. waitforpendingfinalizers ();
In this way, no exception is thrown (even if the terminator thread has ended ).
Of course, the simplest thing is to directly reference the task. Exception attribute:
Note that task. continuewith is used here to avoid directly referencing task variables, so that garbage collection can process this task object!
// Use task. continuewith to avoid directly referencing task variables, so that garbage collection can process this task object!
Task. Factory. startnew () =>
{
Throw new exception ();
}). Continuewith (t => {var exp = T. exception ;});
// Make sure the task is completed
Thread. Sleep (100 );
// Force the spam to receive
GC. Collect ();
// Wait for the terminator to process
GC. waitforpendingfinalizers ();
No exception is thrown.
In addition, you can use taskcontinuationoptions. onlyonfaulted to reference the exception attribute only when an exception occurs (that is, when exception is null, you do not need to reference it again). Code:
Task. Factory. startnew () =>
{
Throw new exception ();
}). Continuewith (t => {var exp = T. exception;}, taskcontinuationoptions. onlyonfaulted );
The last is the taskscheduler. unobservedtaskexception event, which is the last method that can be noticed before all imperceptible exceptions are thrown. The unobservedtaskexceptioneventargs. setobserved method is used to mark an exception as imperceptible.
Code:
Taskschedexception. unobservedtaskexception + = (S, e) =>
{
// Set all imperceptible exceptions to be noticed
E. setobserved ();
};
Task. Factory. startnew () =>
{
Throw new exception ();
});
// Make sure the task is completed
Thread. Sleep (100 );
// Force the spam to receive
GC. Collect ();
// Wait for the terminator to process
GC. waitforpendingfinalizers ();
OK. No exception is thrown.
Note that this phenomenon has changed after. Net 4.5. refer:
. Net 4.5 (C #): update of tasks with imperceptible exceptions