处理消费者队列中的异常

时间:2013-06-23 05:28:50

标签: c# multithreading exception-handling

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

制片:

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

我在.NET 3.5中。

我的API用户将调用Add()方法。在上面的例子中,在方法内部,void work(),我正在捕获异常并记录在那里。

但不是那样,我想抓住并重新抛出用户的异常。 Sametime是在while循环内运行的永久线程,应该通过继续执行队列中的下一个任务从异常中恢复。我的简短问题是 - 如何抛出void work()中发生的异常,但仍然消费者为队列中的下一个任务保持活跃状态​​。

3 个答案:

答案 0 :(得分:1)

继续我们对评论的讨论,你可能会做一些事情,比如收集执行任务队列时发生的所有异常(但是你需要在循环上执行队列),然后把它扔回调用者。 如下所示:

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

我希望这会有所帮助。

答案 1 :(得分:1)

您需要在消费者线程和主线程之间建立某种通信。当消费者遇到异常时,它应该通知主线程并继续下一个任务。

由于您使用的是Winforms,因此通知主线程的最简单方法是使用Invoke。有关示例,请参阅following question

答案 2 :(得分:0)

介绍在任务完成时调用的回调:

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