【问题标题】:Task swallows the exception thrown任务吞下抛出的异常
【发布时间】:2023-03-14 01:51:01
【问题描述】:

在下面的方法中,当 TRY 块中抛出异常时,它正在被吞噬。我怎样才能让它抛出异常,以便将其写入到 catch 块中?日志编写器工作正常。谢谢!

public static bool MonitorQueueEmptyTask(string queueName, CancellationTokenSource tokenSource)
{
    try
    {
        Task<bool> task = Task.Factory.StartNew<bool>(() =>
        {
            while (!QueueManager.IsQueueEmpty(queueName))
            {
                if (tokenSource.IsCancellationRequested)
                {                            
                    break;
                }

                Thread.Sleep(5000);
                throw new Exception("Throwing an error!"); //THIS THROW IS SWALLOWED -- NO LOG WRITTEN ON CATCH
            };

            return true;

        }, tokenSource.Token);
    }
    catch (Exception ex)
    {   
        WriteExceptionToLog(ex.Stack); //it's not that this method doesn't work. it works fine.

        return false;
    }

    return true;
}

【问题讨论】:

  • 你能把try/catch放在任务块里吗?
  • 这行不通,因为外线程退出try时任务可能还没有完成。
  • 谢谢大家!尝试捕获 AggregateException,尝试将 try/catch 移入内部。聚合捕获不起作用,并且将尝试移到内部起作用,但是仅在调试完成后才会发生日志记录。我有这个设置: 在配置中,显然这应该在调试时强制抛出。它没有。
  • 我应该补充一下,这是一种长时间的运行方法,我不能等待。这有点像火,然后忘记,让它做它的事情,但必要时扔掉。

标签: c# .net exception task-parallel-library task


【解决方案1】:

如果您想一劳永逸,您可以使用ContinueWith 附加一个延续。当前的try-catch 根本无法帮助您,因为异常被封装在Task 中。如果这是“一劳永逸”,那么您可以记录异常:

public static Task MonitorQueueEmptyTask(
                         string queueName, CancellationTokenSource tokenSource)
{
    return Task.Factory.StartNew<bool>(() =>
    {
        while (!QueueManager.IsQueueEmpty(queueName))
        {
            if (tokenSource.IsCancellationRequested)
            {                            
                break;
            }

            Thread.Sleep(5000);
            throw new Exception("Throwing an error!");
        };
    }, tokenSource.Token, TaskCreationOptions.LongRunning).ContinueWith(faultedTask =>
    {
        WriteExceptionToLog(faultedTask.Exception); 
    }, TaskContinuationOptions.OnlyOnFaulted); 
}

反过来,这不会在抛出异常后传播异常,但会提供一种记录错误的机制。如果希望异常得到妥善处理,可以注册TaskScheduler.UnobservedTaskException。此外,如果您希望未处理的异常终止您的应用程序,您可以在配置中设置ThrowUnobservedTaskExceptions enabled="true"。当您查看 task.Exception 属性时,ContinueWith 将考虑“已处理”异常。

【讨论】:

  • 请注意,您必须专门访问 task.Exception 属性才能将异常视为已处理。
  • Docs 说在 ThrowUnobservedTaskExceptions 上设置启用 true “终止未处理任务异常的运行进程。”。这里不需要吗?
  • @Vimes 仅当您希望异常在未处理时终止,否则不。为了清楚起见,重新措辞。
【解决方案2】:

异常没有被吞掉;只是它不会发生在执行 try/catch 块的线程上,而是发生在单独的 Task 线程上。

如果你没有观察到任务的结果或异常,当任务最终被垃圾回收时,它会抛出一个异常,说没有观察到任务。除非您通过处理 TaskScheduler.UnobservedTaskException 来捕捉到这一点,否则它将使进程崩溃。

【讨论】:

    【解决方案3】:

    我也有这个问题,我真的不喜欢 App.config 的整个想法,所以可以提供另一种解决方案来防止异常消失:)

    保存异常,然后在 Task.Run 完成后将其抛出,例如

    private async void Function() {
        Exception save_exception = null;
    
        await Task.Run(() => {
            try {
                // Do Stuff
            } catch (Exception ex) {
                save_exception = ex;
            }
        }).ContinueWith(new Action<Task>(task => {
            if (save_exception != null)
                throw save_exception;
    
            // Do Stuff 
        }));
    }
    

    【讨论】:

      猜你喜欢
      • 2019-11-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多