3

I'm having trouble understanding how Exceptions are handled in TPL. The following code should illustrate my problem.

using System;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using System.Threading.Tasks;

namespace WebDLApp
{
    class Program
    {
        static void Main(string[] args)
        {
            TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; // Not catching exception

            List<string> sites = new List<string>{ "http://microsoft.com", "http://yahoo.com", "http://facebook.com", "http://amazon.com", "http://foooo", "http://aol.com", "http://ask.com", "http://wikipedia.org" };
            List<Task<string>> tasks = new List<Task<string>>();


            foreach (string site in sites)
            {
                tasks.Add(Task.Factory.StartNew<string>((wsite) =>
                    {
                        using (WebClient wc = new WebClient())
                        {
                            wc.DownloadString((string)wsite); // Thrown here, always
                            return (string)wsite;
                        }
                    }, site)
                );
            }


            Task.WaitAll(tasks.ToArray()); // Can't catch here

            int counter = 1;
            foreach (var task in tasks)
            {

                Console.WriteLine(counter.ToString() + task.Result); // Can't catch here either
                counter++;
            }

            Console.ReadLine();
        }

        static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) // Never called
        {
            Console.WriteLine(e.Exception.Message);
            e.SetObserved();
        }
    }
}

From what I understand, using the TaskScheduler.UnobservedTaskException event object is a pretty good way to help handle exceptions from tricky third-party libraries. However, the Exception seems to be always thrown within the Task. It looks as if it never bubbles up to be caught by the TaskScheduler (or whatever facility handles TaskExceptions).

For code brevity, I've omitted possible try/catch locations and marked them with a comment.

I'm expecting the TaskScheduler_UnobservedTaskException event handler to print to the console and observe the Exception. However, once the execution reaches the Result of the task, a WebException is thrown from within the Task.

Matt
  • 515
  • 3
  • 16
  • You haven't said what behavior you're actually seeing. I'm guessing that your `Task.WaitAll` call is throwing an `AggregateException`. If so, that would be expected behavior: you don't have any unobserved exceptions. `Wait`/`WaitAll`/`WaitAny` will propagate any exceptions from the `Task`s you're waiting on, at which point it's no longer unobserved. – Joe White Jun 03 '12 at 21:39

1 Answers1

2

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

L.B
  • 114,136
  • 19
  • 178
  • 224
  • 2
    Thanks. I understand now that I was thinking about "last chance" in the wrong way. I thought it meant that if the exception went unhandled, it would just bubble up to the TheadScheduler and be done. I understand now that it refers to the condition where the Task was not checked, joined, or touched and it's nearing collection. – Matt Jun 03 '12 at 22:18
  • @L.B Hi, there is a way to get "last change" behavior ? I wrote something to catch all errors but having troubles here: http://stackoverflow.com/questions/11831844/unobservedtaskexception-being-throw-but-it-is-handled-by-a-taskscheduler-unobser – newway Aug 08 '12 at 15:57