C# 处理使用者队列中的异常

C# 处理使用者队列中的异常,c#,multithreading,exception-handling,C#,Multithreading,Exception Handling,制作人: public class ProducerConsumerQueue { public void EnqueueTask(MyTask task) { } void Work() { while (true) { try { // my task goes here

制作人:

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);
            }
        }
    }
}
我在.NET3.5

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


但与此相反,我希望捕获异常并将其重新提交给用户。Sametime是在while循环中运行的永久线程,应该通过继续执行队列中的下一个任务来从异常中恢复。我的简短问题是-我将如何抛出在void work()中发生的异常,但消费者仍然可以继续执行队列中的下一个任务

继续我们的评论讨论,您可能会做一些事情,比如收集执行任务队列时发生的所有异常(但是您需要按周期执行队列),然后将其发回调用方。 比如:

public void Add()
{
    MyTask task = new MyTask();
    new ProducerConsumerQueue().EnqueueTask(task);
}
public void ExecuteAllTasks()
{
var exceptions=新列表();
IEnumerable tasks=GetQueuedTasks();//获取所有任务(或者可能将它们传递给方法)。。。
foreach(任务中的MyTask任务)
{
尝试
{
//在这里执行您的任务。。。
}
捕获(例外情况除外)
{
//收集所有异常
例外情况。添加(ex);
}            
}
//一次抛出所有错误
if(exceptions.Any())
抛出新的AggregateException(_异常);
}

我希望这会有所帮助。

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


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

引入一个在任务完成时调用的回调:

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);
}
公共接口IComplectionState
{
公共ITask任务{get;set;}
公共异常{get;set;}
}
公共类CompletionState:ICompletionState
{
公共ITask任务{get;set;}
公共异常{get;set;}
公共操作回调{get;set;}
}
公共类ProducerConsumerQueue
{
ConcurrentQueue_tasks=新建ConcurrentQueue();
公共无效排队任务(ITask任务、操作回调)
{
_Enqueue(新的CompletionState{Task=Task,Callback=Callback});
}
无效工作()
{                           
while(true)
{
完成状态cs;
尝试
{
if(!\u tasks.TryDequeue(out cs))
继续;
cs.Task.Execute();
cs.回调(cs);
}
捕获(例外情况除外)
{
cs.Exception=ex;
cs.回调(cs);
}
}
}
}

这是Windows服务吗?WPF应用程序?Winforms?ASP.NET?@Tim感谢您的编辑。现在更容易阅读。老实说,这可能不是你想要的答案,但是。。。只要抛出异常,不要这样做。记录异常并继续,或者将其重新抛出到API客户端。如果您有一个场景,其中您有一个队列要在循环中执行,您可以在循环完成后执行所有任务->收集异常->立即抛出它们。@DimitarDimitrov我无法记录异常,因为它是API。我会让调用Add()的用户捕获异常并记录它。我尊重您的想法,即当我想让任务继续执行队列中的下一个任务时,不要在work()内部抛出任何异常。您每个周期收集所有异常并在周期结束后立即抛出它们的想法是好的。我该怎么做?有什么设计建议吗?。我的主要目标是两个。1) 让用户知道在work()中发生的任何异常2)work()应该从任何异常中恢复并继续执行下一个任务谢谢。如何以线程安全的方式使void Add()(或实现void Add()的类)知道异常列表。实现public void Add()的生产者类是一个API。用户将使用新的ProducerClass()。添加(…)。该API现在是通用的,不依赖于Winforms特定的DLL(尽管API使用者主要是Winforms)。在不使用Invoke的情况下,如何将异常从使用者传递给生产者。向API添加事件处理程序-当抛出异常时,使用者将触发事件。在Winforms代码中,实现事件处理程序,并让它使用Invoke.Ok调用主线程。通过这种方式,我可以将API中的异常传递给API用户。但是我如何在生产者和生产者之间传递异常(如上面的示例代码中所示)消费者触发事件,而不是生产者。制作人可以监听事件,然后触发自己的事件。我猜你的API用户根本不知道消费者,所以他们无法连接到消费者事件。谢谢。所以我把你的答案和迪米特罗夫的建议结合起来。在方法void work()中,我将收集列表中的所有异常。消费者将使用异常列表触发事件。制片人将订阅该活动。对于我的API用户,我必须从生产者那里触发一个单独的事件。那么,这是我们能为这个问题实现的最佳设计吗?没有别的办法吗?
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);
            }
        }
    }
}