Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何捕获/观察任务引发的未处理异常_C#_.net_Wpf_Exception Handling_Task Parallel Library - Fatal编程技术网

C# 如何捕获/观察任务引发的未处理异常

C# 如何捕获/观察任务引发的未处理异常,c#,.net,wpf,exception-handling,task-parallel-library,C#,.net,Wpf,Exception Handling,Task Parallel Library,我正在尝试在我的应用程序(错误报告解决方案)中记录/报告所有未处理的异常。我遇到了一个总是无法处理的情况。我想知道如何以未经处理的方式捕获此错误。请注意,今天早上我做了大量的研究,尝试了很多东西。。是的,我见过,还有很多。我只是在寻找一个通用的解决方案来记录未处理的异常 我在控制台测试应用程序主方法中有以下代码: Task.Factory.StartNew(TryExecute); 或 以及以下方法: private static void TryExecute() { throw ne

我正在尝试在我的应用程序(错误报告解决方案)中记录/报告所有未处理的异常。我遇到了一个总是无法处理的情况。我想知道如何以未经处理的方式捕获此错误。请注意,今天早上我做了大量的研究,尝试了很多东西。。是的,我见过,还有很多。我只是在寻找一个通用的解决方案来记录未处理的异常

我在控制台测试应用程序主方法中有以下代码:

Task.Factory.StartNew(TryExecute);

以及以下方法:

private static void TryExecute() {
   throw new Exception("I'm never caught");
}
我已经尝试在我的应用程序中连接到以下内容,但从未调用它们

AppDomain.CurrentDomain.UnhandledException
TaskScheduler.UnobservedTaskException
Dispatcher.UnhandledException
Application.Current.DispatcherUnhandledException
System.Windows.Forms.Application.ThreadException
在我最初发现此错误的Wpf应用程序中,我也连接到这些事件,但从未调用它

AppDomain.CurrentDomain.UnhandledException
TaskScheduler.UnobservedTaskException
Dispatcher.UnhandledException
Application.Current.DispatcherUnhandledException
System.Windows.Forms.Application.ThreadException
唯一被调用的处理程序是:

AppDomain.CurrentDomain.FirstChanceException

但这不是一个有效的解决方案,因为我只想报告未捕获的异常(并非每个异常都像FirstChanceException一样在执行/解析任何捕获块之前被调用。

任务调度程序。未观察到的taskeException事件应该会给你想要的,正如你上面所说的。是什么让你认为它没有被触发

异常被任务捕获,然后在特定情况下重新抛出,但不是立即抛出。任务中的异常以几种方式重新抛出(在我看来,可能还有更多)

  • 尝试访问结果时(
    Task.result
  • 调用任务上的
    Wait()
    Task.WaitOne()
    Task.WaitAll()
    或其他相关的
    Wait
    方法
  • 当您试图在不显式查看或处理异常的情况下处置任务时
  • 如果您执行上述任一操作,则无论代码运行在哪个线程上,都会重新调用异常,并且不会调用该事件,因为您将观察异常。如果
    中没有代码,请尝试{}catch{}
    ,您将触发
    AppDomain.CurrentDomain.UnhandledException
    ,这听起来像是发生了什么

    重新引发异常的另一种方式是:

    • 当您不执行上述任何操作时,任务仍会将异常视为未观察到的异常,并且任务正在完成。这是为了让您知道有一个您未看到的异常而进行的最后努力
    如果是这种情况,并且由于终结器是不确定的,您是否在等待GC发生,以便将那些未观察到异常的任务放入终结器队列,然后再次等待它们被终结

    编辑:对此进行一点讨论。讨论事件存在的原因,这可能会让您了解如何正确使用该事件。

    我使用来自MSDN的命令捕获所有异常,包括来自使用TPL的其他线程的异常:

    
    public class LimitedConcurrencyLevelTaskScheduler : TaskScheduler
    {
        /// Whether the current thread is processing work items.
        [ThreadStatic]
        private static bool currentThreadIsProcessingItems;
    
        /// The list of tasks to be executed.
        private readonly LinkedList tasks = new LinkedList(); // protected by lock(tasks)
    
        private readonly ILogger logger;
    
        /// The maximum concurrency level allowed by this scheduler.
        private readonly int maxDegreeOfParallelism;
    
        /// Whether the scheduler is currently processing work items.
        private int delegatesQueuedOrRunning; // protected by lock(tasks)
    
        public LimitedConcurrencyLevelTaskScheduler(ILogger logger) : this(logger, Environment.ProcessorCount)
        {
        }
    
        public LimitedConcurrencyLevelTaskScheduler(ILogger logger, int maxDegreeOfParallelism)
        {
            this.logger = logger;
    
            if (maxDegreeOfParallelism Gets the maximum concurrency level supported by this scheduler.
        public override sealed int MaximumConcurrencyLevel
        {
            get { return maxDegreeOfParallelism; }
        }
    
        /// Queues a task to the scheduler.
        /// The task to be queued.
        protected sealed override void QueueTask(Task task)
        {
            // Add the task to the list of tasks to be processed.  If there aren't enough
            // delegates currently queued or running to process tasks, schedule another.
            lock (tasks)
            {
                tasks.AddLast(task);
    
                if (delegatesQueuedOrRunning >= maxDegreeOfParallelism)
                {
                    return;
                }
    
                ++delegatesQueuedOrRunning;
    
                NotifyThreadPoolOfPendingWork();
            }
        }
    
        /// Attempts to execute the specified task on the current thread.
        /// The task to be executed.
        /// 
        /// Whether the task could be executed on the current thread.
        protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
        {
            // If this thread isn't already processing a task, we don't support inlining
            if (!currentThreadIsProcessingItems)
            {
                return false;
            }
    
            // If the task was previously queued, remove it from the queue
            if (taskWasPreviouslyQueued)
            {
                TryDequeue(task);
            }
    
            // Try to run the task.
            return TryExecuteTask(task);
        }
    
        /// Attempts to remove a previously scheduled task from the scheduler.
        /// The task to be removed.
        /// Whether the task could be found and removed.
        protected sealed override bool TryDequeue(Task task)
        {
            lock (tasks)
            {
                return tasks.Remove(task);
            }
        }
    
        /// Gets an enumerable of the tasks currently scheduled on this scheduler.
        /// An enumerable of the tasks currently scheduled.
        protected sealed override IEnumerable GetScheduledTasks()
        {
            var lockTaken = false;
    
            try
            {
                Monitor.TryEnter(tasks, ref lockTaken);
    
                if (lockTaken)
                {
                    return tasks.ToArray();
                }
                else
                {
                    throw new NotSupportedException();
                }
            }
            finally
            {
                if (lockTaken)
                {
                    Monitor.Exit(tasks);
                }
            }
        }
    
        protected virtual void OnTaskFault(AggregateException exception)
        {
            logger.Error(exception);
        }
    
        /// 
        /// Informs the ThreadPool that there's work to be executed for this scheduler.
        /// 
        private void NotifyThreadPoolOfPendingWork()
        {
            ThreadPool.UnsafeQueueUserWorkItem(ExcuteTask, null);
        }
    
        private void ExcuteTask(object state)
        {
            // Note that the current thread is now processing work items.
            // This is necessary to enable inlining of tasks into this thread.
            currentThreadIsProcessingItems = true;
    
            try
            {
                // Process all available items in the queue.
                while (true)
                {
                    Task item;
                    lock (tasks)
                    {
                        // When there are no more items to be processed,
                        // note that we're done processing, and get out.
                        if (tasks.Count == 0)
                        {
                            --delegatesQueuedOrRunning;
                            break;
                        }
    
                        // Get the next item from the queue
                        item = tasks.First.Value;
                        tasks.RemoveFirst();
                    }
    
                    // Execute the task we pulled out of the queue
                    TryExecuteTask(item);
    
                    if (!item.IsFaulted)
                    {
                        continue;
                    }
    
                    OnTaskFault(item.Exception);
                }
            }
            finally
            {
                // We're done processing items on the current thread
                currentThreadIsProcessingItems = false;
            }
        }
    }
    
    并使用反射将TaskScheduler的“注册”设置为默认设置:

    
    public static class TaskLogging
    {
        private const BindingFlags StaticBinding = BindingFlags.Static | BindingFlags.NonPublic;
    
        public static void SetScheduler(TaskScheduler taskScheduler)
        {
            var field = typeof(TaskScheduler).GetField("s_defaultTaskScheduler", StaticBinding);
            field.SetValue(null, taskScheduler);
    
            SetOnTaskFactory(new TaskFactory(taskScheduler));
        }
    
        private static void SetOnTaskFactory(TaskFactory taskFactory)
        {
            var field = typeof(Task).GetField("s_factory", StaticBinding);
            field.SetValue(null, taskFactory);
        }
    }
    

    有趣的是..我对任务没有做太多的处理,但是对于其他所有事情,应用程序中未处理的异常总是调用AppDomain UnhandledException。也许任务API会自动捕获任务中抛出的所有异常,并以某种方式重新路由它们..如果我等待任务,我会看到它击中UnhandledException。但是我不能arantee,开发人员在使用此报告库时会这样做。谢谢,这听起来像是在发生什么。我在处理程序中设置了断点,但它从未被触发。如果我等待()它,我会在未处理的异常处理程序中捕获异常。我假设,因为我没有(正在调试提交给我们的示例),GC还没有运行,这就是为什么我从来没有抓到它的原因。谢谢你的详细回复。这是一个巨大的帮助。