Ilyas
Ilyas

Reputation: 759

Task Parallel Library and Exceptions?

Should I do this?

int x = 0;
Task<int> calc = Task.Factory.StartNew (() => 7 / x);
try
{
  Console.WriteLine (calc.Result);
}
catch (AggregateException aex)
{
  Console.Write (aex.InnerException.Message);  // Attempted to divide by 0
}

or this?

int x = 0;
try
{
  Task<int> calc = Task.Factory.StartNew (() => 7 / x);
  Console.WriteLine (calc.Result);
}
catch (AggregateException aex)
{
  Console.Write (aex.InnerException.Message);  // Attempted to divide by 0
}

If the task starts immediately and before we are in the try catch block then, we wont catch it...!?

Upvotes: 1

Views: 135

Answers (4)

Adi Lester
Adi Lester

Reputation: 25201

Tasks have an Exception property that holds the exception that was thrown during the task's execution, if there was one. This means you can do this:

int x = 0;
Task<int> calc = Task.Factory.StartNew(() => 7 / x);
calc.ContinueWith(t => Console.WriteLine ("Got exception of {0}", t.Exception),
                  TaskContinuationOptions.OnlyOnFaulted);

Upvotes: 0

Craig Gidney
Craig Gidney

Reputation: 18276

In this case you don't need to include it in the try block. Task.Factory.StartNew will not throw an exception unless you pass it a null Action to execute. Any exceptions thrown by the action being run will be propagated into the task, not up the stack.

More generally, I think the rule of thumb for these types of situations is that the exception should always go into the task. For example, if you throw an exception before the first await in an async method, it also goes into the resulting task instead of propagating up the stack.

Upvotes: 0

usr
usr

Reputation: 171178

You want to execute as little code as possible in your try block because the more you include the more spurious, unwanted exception you will catch. You only want to catch those of which you know they are benign. You don't want to swallow bugs.

Therefore either do this:

var task = ...;
int result;
try { result = task.Result; } //just catch task.Result
...

or even this:

if (task.Exception != null) { /* error */ }
else { /* use task.Result */ }

Upvotes: 0

svick
svick

Reputation: 244777

One of the points of using Task is that you mostly don't have to worry about things like that.

As you noticed, there are two possible orders of events with your first sample:

  1. StartNew() is called from thread A.
  2. Result getter is called from thread A. The task didn't finish yet, so the call blocks.
  3. The delegate is executed on the ThreadPool thread B and throws DivideByZeroException.
  4. Thread A wakes up and Result throws AggregateException.

The second possibility is:

  1. StartNew() is called from thread A.
  2. The delegate is executed on the ThreadPool thread B and throws DivideByZeroException.
  3. Result getter is called from thread A. The task already finished, so the call immediately throws AggregateException.

As you see, in both cases the Result getter throws the exception, it doesn't matter in what order did the code execute.

Your second version would only make sense if StartNew() could throw AggregateException, but that never happens.

Let me repeat: TPL takes care of all the synchronization, you don't have to worry about it here.

Upvotes: 1

Related Questions