Ram
Ram

Reputation: 46

Handle exception in consumer queue

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.

Upvotes: 0

Views: 1151

Answers (3)

jgauffin
jgauffin

Reputation: 101150

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);
            }
        }
    }
}

Upvotes: 0

zmbq
zmbq

Reputation: 39023

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.

Upvotes: 1

Dimitar Dimitrov
Dimitar Dimitrov

Reputation: 15158

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.

Upvotes: 1

Related Questions