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
,这听起来像是发生了什么
重新引发异常的另一种方式是:
- 当您不执行上述任何操作时,任务仍会将异常视为未观察到的异常,并且任务正在完成。这是为了让您知道有一个您未看到的异常而进行的最后努力
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还没有运行,这就是为什么我从来没有抓到它的原因。谢谢你的详细回复。这是一个巨大的帮助。