Question

I am executing some polling IO loops in separate Tasks. Those loops may encounter exceptions. If one encounters an exception, I want to alert the caller so that it can:

  1. log it
  2. kill all IO threads
  3. reset the connection
  4. restart IO threads

The UI must remain responsive. What is the preferred method of handling this scenario? I've include an illustrative program below.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TaskExceptionCatching
{
    class Program
    {
        static void Main(string[] args)
        {
            startLoops();

            System.Console.WriteLine("Type 'Exit' when you're ready to stop.");
            while (System.Console.ReadLine() != "Exit")
            {
                System.Console.WriteLine("Seriously, just type 'Exit' when you're ready to stop.");
            }
        }

        static private void startLoops()
        {
            System.Console.WriteLine("Starting fizzLoop.");
            var fizzTask = Task.Factory.StartNew(new Action(fizzLoop));

            System.Console.WriteLine("Starting buzzLoop.");
            var buzzTask = Task.Factory.StartNew(new Action(buzzLoop));
        }

        static private void fizzLoop()
        {
            while (true)
            {
                //simulate something error prone, like some risky IO
                System.Threading.Thread.Sleep(200);
                bool isErr = (new Random().Next(1, 100) == 10);
                if (isErr)
                    throw new Exception("Fizz got an exception.");
            }
        }

        static private void buzzLoop()
        {
            while (true)
            {
                //simulate something error prone, like some risky IO
                System.Threading.Thread.Sleep(200);
                bool isErr = (new Random().Next(1, 100) == 10);
                if (isErr)
                    throw new Exception("Buzz got an exception.");
            }
        }
    }
}
Was it helpful?

Solution

This might be one of the rare cases when an async void method could be convenient:

static async void StartAndMonitorAsync(Func<Task> taskFunc)
{
    while (true)
    {
        var task = taskFunc();
        try
        {
            await task;
            // process the result if needed
            return;
        }
        catch (Exception ex)
        {
            // log the error
            System.Console.WriteLine("Error: {0}, restarting...", ex.Message);
        }
        // do other stuff before restarting (if any)
    }
}

static private void startLoops()
{
    System.Console.WriteLine("Starting fizzLoop.");
    StartAndMonitorAsync(() => Task.Factory.StartNew(new Action(fizzLoop)));

    System.Console.WriteLine("Starting buzzLoop.");
    StartAndMonitorAsync(() => Task.Factory.StartNew(new Action(buzzLoop)));
}

If you can't use async/await, a similar logic can be implemented using Task.ContinueWith.

If startLoops can be called multiple times (while tasks are already "in-flight"), you'd need to add cancellation logic to StartAndMonitorAsync and the tasks it sarts, using CancelltionToken (more details in "A pattern for self-cancelling and restarting task").

OTHER TIPS

From MSDN "The Task infrastructure wraps them in an AggregateException instance. The AggregateException has an InnerExceptions property that can be enumerated to examine all the original exceptions that were thrown, and handle (or not handle) each one individually. Even if only one exception is thrown, it is still wrapped in an AggregateException." For more information you can check this

try
{
System.Console.WriteLine("Starting fizzLoop.");
            var fizzTask = Task.Factory.StartNew(new Action(fizzLoop));

            System.Console.WriteLine("Starting buzzLoop.");
            var buzzTask = Task.Factory.StartNew(new Action(buzzLoop));

}
catch (AggregateException ae)
{
    // Assume we know what's going on with this particular exception. 
    // Rethrow anything else. AggregateException.Handle provides 
    // another way to express this. See later example. 
    foreach (var e in ae.InnerExceptions)
    {
        if (e is MyCustomException)
        {
            Console.WriteLine(e.Message);
        }
        else
        {
            throw;
        }
    }

}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top