0
public class ProducerConsumerQueue
{
    public void EnqueueTask(MyTask task)
    {

    }

    void Work()
    {                           
        while (true)
        {
            try
            {
                // my task goes here
                Thread.Sleep(2000);     
            }
            catch(Exception ex)
            {
                Log(ex);
            }
        }
    }
}

Producer:

public void Add()
{
    MyTask task = new MyTask();
    new ProducerConsumerQueue().EnqueueTask(task);
}

I'm in .NET 3.5.

Add() method will be called by my API users. In the example above, inside the method, void work(), I'm catching the exception and logging there.

But instead of that, I would like to catch and rethrow the exception to the user. Sametime, the permanent thread that run inside the while loop, should recover from the exception by continue to the next task in the queue. My short question is - How will I throw exception that happen inside void work(), but still the consumer stay alive for next task in the queue.

Dimitar Dimitrov
  • 14,868
  • 8
  • 51
  • 79
Ram
  • 44
  • 1
  • 13
  • Is this a Windows Service? A WPF application? Winforms? ASP.NET? – zmbq Jun 23 '13 at 05:32
  • @Tim Thanks for editing. It's more readable now. – Ram Jun 23 '13 at 05:57
  • Honestly, this might not be the answer you're looking for, but ... just throw the Exception, don't do this. Either log the Exception and move on or re-throw it back to your API client. – Dimitar Dimitrov Jun 23 '13 at 06:16
  • If you have a scenario where you have a queue that you want to execute on cycles, you can execute all the tasks -> collect the exceptions -> throw them at once after the cycle is done. – Dimitar Dimitrov Jun 23 '13 at 06:20
  • @DimitarDimitrov I cannot log the exception since it's API. I would let the user's who call Add() catch the exception and log it. I respect your idea of not to throw any exception inside work() when I want to let the task to continue to the next task in the queue. Your idea of collecting all the exception per cycle and throw them at once after the cycle is good. How will I do that ? any design suggestions?. My primary aim is two. 1) Let the users aware of the any exception that happen inside work() 2) work() should recover from any exception and continue with the next task – Ram Jun 23 '13 at 07:11

3 Answers3

1

Continuing our discussion from the comments, you could possibly do something like collecting all the exceptions occurring when executing a queue of tasks (however you need to execute the queue on cycles) and then throwing it back to the caller. So something like:

public void ExecuteAllTasks()
{
    var exceptions = new List<Exception>();
    IEnumerable<MyTask> tasks = GetQueuedTasks(); // get all tasks (or possibly pass them to the method) ...
    foreach (MyTask task in tasks)
    {
        try
        {
            // execute your tasks here ...
        }
        catch (Exception ex)
        {
            // collect all the exceptions
            exceptions.Add(ex);
        }            
    }

    // throw all the errors at once
    if (exceptions.Any())
        throw new AggregateException(_exceptions);
}

I hope this helps.

Dimitar Dimitrov
  • 14,868
  • 8
  • 51
  • 79
  • Thank you. How will I make void Add() (or the class that implement void Add()) aware of exceptions list in thread safe way. – Ram Jun 23 '13 at 08:18
1

You need to establish some sort of communications between your consumer threads and the main thread. When a consumer encounters an exception, it should notify the main thread and move on to the next task.

Since you're using Winforms, the easiest way to inform the main thread is to use Invoke. See the following question for an example.

Community
  • 1
  • 1
zmbq
  • 38,013
  • 14
  • 101
  • 171
  • The producer class that implement public void Add() is an API. User will use new ProducerClass().Add(...). The API is generic now without any dependency on Winforms specific dlls (although api consumer's are mostly winforms though). Without using Invoke, how may I communicate the exceptions from consumer to producer. – Ram Jun 23 '13 at 08:48
  • Add an event handler to the API - the consumers will fire the event when an exception is thrown. In your Winforms code, implement the event handler and have it call the main thread using Invoke. – zmbq Jun 23 '13 at 08:52
  • Ok. This way I can communicate the exceptions from my API to API users. But how will I communicate the exceptions between the Producer and ProducerConsumerQueue( as in the sample code above) – Ram Jun 23 '13 at 09:00
  • The *consumer* fires the event, not the producer. The producer can listen to the event, and then fire its own event. I guess your API users are not aware of the consumer at all, so they can't hook into consumer events. – zmbq Jun 23 '13 at 09:02
  • Thank you. So I combine your answer with the suggestion offered by Dimitar Dimitrov. In method void work(), I'll collect all exception in a List. Consumer will fire the event with exception list. Producer will subscribe to event. To my API user's I've to fire a separate event from the producer. So is this the best design we can achieve for this problem? No other way ? – Ram Jun 23 '13 at 09:23
0

Introduce a callback which is invoked when the task has completed:

public interface ICompletionState
{
    public ITask Task { get; set; }
    public Exception Exception { get; set; }
}
public class CompletionState : ICompletionState
{
    public ITask Task { get; set; }
    public Exception Exception { get; set; }
    public Action<ICompletionState> Callback { get; set; }
}

public class ProducerConsumerQueue
{
    ConcurrentQueue<CompletionState> _tasks = new ConcurrentQueue<CompletionState>();

    public void EnqueueTask(ITask task, Action<ICompletionState> callback)
    {
        _tasks.Enqueue(new CompletionState{ Task = task, Callback = callback });
    }

    void Work()
    {                           
        while (true)
        {
            CompletionState cs;
            try
            {
                if (!_tasks.TryDequeue(out cs))
                    continue;

                cs.Task.Execute();
                cs.Callback(cs);
            }
            catch(Exception ex)
            {
                cs.Exception = ex;
                cs.Callback(cs);
            }
        }
    }
}
jgauffin
  • 99,844
  • 45
  • 235
  • 372