UI 线程中执行的异步方法中的异常导致应用程序崩溃

Pix*_*xar 3 wpf exception ui-thread task-parallel-library async-await

当我await使用抛出异常的方法时,try/catch 不会使应用程序免于崩溃。

有一种投掷方法

void CurrentStep.Process(CancellationToken cancellationToken)
{
    throw new Exception();
}
Run Code Online (Sandbox Code Playgroud)

它是通过以下方式从 UI 线程调用的:ICommand.Execute()

ProcessCurrentStepCommand = AsyncCommandFactory.Create(async cancellationToken => 
{
    try
    {
        await Task.Run(() => CurrentStep.Process(cancellationToken));
    }
    catch {}

    CurrentStep = CurrentStep.NextStepViewModel;
});
Run Code Online (Sandbox Code Playgroud)

ProcessCurrentStepCommand绑定到 UI 上的按钮。当我单击按钮时,我的应用程序崩溃了。我觉得在 UI 线程上抛出异常存在一个普遍的问题,但同时我不明白为什么 catch 块不能让我免于异常。

我现在找到了唯一适合我的方法:

await Task.Factory.StartNew(
    action: () => CurrentStep.Process(cancellationToken),
    creationOptions: TaskCreationOptions.LongRunning);
Run Code Online (Sandbox Code Playgroud)

但看起来很丑。如果我将来忘记了我想用这段代码做什么,我可能会认为我需要清理它,并因某些异常而导致整个应用程序崩溃而陷入麻烦。


当处于调试模式时,一切都表现良好。

  1. 异常源的原始中断。 在此输入图像描述

调用堆栈

UI.exe !UI.Steps.ViewModels.SvmConnectionViewModel.Process(System.Threading.CancellationToken CancellationToken)
UI.exe !UI.MainViewModel..ctor.AnonymousMethod__1() 第 18 行 mscorlib.dll !System.Threading.Tasks.Task.InnerInvoke ()第2911行 mscorlib.dll!System.Threading.Tasks.Task.Execute()第2523行 mscorlib.dll!System.Threading.Tasks.Task.ExecutionContextCallback(object obj)第2888行 mscorlib.dll!System.Threading.ExecutionContext。 RunInternal(System.Threading.ExecutionContextexecutionContext,System.Threading.ContextCallback回调,对象状态,布尔保留SyncCtx)第581行
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContextexecutionContext,System.Threading.ContextCallback回调,对象状态,boolserveSyncCtx)第531行
mscorlib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref System.Threading.Tasks.Task currentTaskSlot)第2853行 mscorlib.dll!System.Threading.Tasks.Task.ExecuteEntry(bool bPreventDoubleExecution) )第2792行 mscorlib.dll!System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()第2729行
mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch()第830行 mscorlib.dll!System.Threading._ThreadPoolWaitCallback .PerformWaitCallback() 第 1171 行

  1. 下一个中断发生在TaskAwaiter.cs 的ThrowForNonSuccess()方法中:NotImplementedException occured: A first chance exception of type 'System.NotImplementedException' occurred in mscorlib.dll在此输入图像描述

调用堆栈

mscorlib.dll !System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(System.Threading.Tasks.Task 任务) 第 180 行
mscorlib.dll !System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task 任务) 第 170 行
mscorlib.dll !System.Runtime.CompilerServices.TaskAwaiter.GetResult() 第 125 行
UI.exe !UI.MainViewModel..ctor(System.Threading.CancellationToken CancelToken) 第 18 行
[恢复异步方法]
mscorlib.dll !System.Runtime. CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(对象stateMachine)第1065行
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContextexecutionContext,System.Threading.ContextCallback回调,对象状态,布尔保留SyncCtx)第581行
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContextexecutionContext,System.Threading.ContextCallback回调,对象状态,boolpreserveSyncCtx)第531行
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run()第1045行
mscorlib.dll !System.Runtime.CompilerServices.AsyncMethodBuilderCore.OutputAsyncCausalityEvents>.AnonymousMethod__0() 第 973 行
mscorlib.dll !System.Runtime.CompilerServices.AsyncMethodBuilderCore.ContinuationWrapper.Invoke() 第 1085 行
mscorlib.dll !System.Runtime.CompilerServices.TaskAwaiter .outputwaitetWevents.AnonymousMethod__0()行301
mscorlib.dll!system.runtime.compilerServices.asyncmethodbuildercore.continuationwrrapper.invoke.invoke()行1085
mscorlib.dll!system.thread! 3()行470
mscorlib.dll! System.Threading.Tasks.SynchronizationContextAwaitTaskContinuation..cctor.AnonymousMethod__6(对象状态) 第 393 行
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate 回调,对象 args,int numArgs) 第 118 行 未知
WindowsBase.dll! MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(object source, System.Delegate method, object args, int numArgs, System.Delegate catchHandler) 第 41 行未知
WindowsBase.dll !System.Windows.Threading.DispatcherOperation.InvokeImpl() 第 583 行未知
WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(对象状态)第528行未知
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContextexecutionContext,System.Threading.ContextCallback回调,对象状态,boolpreserveSyncCtx)第581行
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContextexecutionContext, System.Threading.ContextCallback 回调,对象状态,bool keepSyncCtx) 第 531 行
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContextexecutionContext,System.Threading.ContextCallback 回调,对象状态) 第 520 行
WindowsBase.dll! System.Windows.Threading.DispatcherOperation.Invoke() 第 441 行未知
WindowsBase.dll !System.Windows.Threading.Dispatcher.ProcessQueue() 第 2227 行未知
WindowsBase.dll !System.Windows.Threading.Dispatcher.WndProcHook(System.IntPtr hwnd 、 int msg、System.IntPtr wParam、System.IntPtr lParam、ref bool 已处理)第 2480 行未知
WindowsBase.dll !MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd、int msg、System.IntPtr wParam、System.IntPtr lParam ,ref bool 已处理)第 345 行未知
WindowsBase.dll !MS.Win32.HwndSubclass.DispatcherCallbackOperation(object o) 第 494 行未知 WindowsBase.dll !System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate 回调,对象 args,int numArgs ) 第 111 行未知 WindowsBase.dll !MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(object source, System.Delegate method, object args, int numArgs, System.Delegate catchHandler) 第 41 行未知
WindowsBase.dll !System.Windows.Threading。 Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority优先级,System.TimeSpan超时,System.Delegate方法,对象args,int numArgs)第1447行未知 WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr hwnd,int) msg、System.IntPtr wParam、System.IntPtr lParam) 第 398 行未知
[本机到托管转换]
[托管到本机转换]
WindowsBase.dll !System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame 框架)第2281行未知的
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame框架)第369行未知的
WindowsBase.dll!System.Windows.Threading.Dispatcher.Run()第328行未知的
PresentationFramework.dll!System.Windows.Application.RunDispatcher(对象忽略)第2745行
PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window窗口)第1841行 PresentationFramework.dll!System.Windows.Application.Run(System.Windows.Window window) 第 261 行 PresentationFramework.dll !System.Windows.Application.Run() 第 222 行 UI.exe!UI.App.Main() [本机到托管转换]
mscorlib.dll !System.AppDomain.ExecuteAssembly(string assemblyFile, System.Security.Policy.Evidence assemblySecurity, string[] args) 第 2031 行
Microsoft.VisualStudio.HostingProcess.Utilities.dll !Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()未知 mscorlib.dll !System.Threading.ThreadHelper.ThreadStart_Context(对象状态) 第 74 行
mscorlib.dll !System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContextexecutionContext、System.Threading.ContextCallback 回调、对象状态、boolserveSyncCtx)第581行
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContextexecutionContext,System.Threading.ContextCallback回调,对象状态,boolpreserveSyncCtx)第531行
mscorlib.dll!System.Threading.ExecutionContext.Run(System。 Threading.ExecutionContextexecutionContext,System.Threading.ContextCallback回调,对象状态)第520行
mscorlib.dll!System.Threading.ThreadHelper.ThreadStart()第111行
[异步调用]
UI.exe!UI.Commands.AsyncCommandFactory.Create(System。 Threading.CancellationToken 令牌)第 27 行
[异步调用]
UI.exe !UI.NotifyTaskCompletion.WatchTaskAsync(System.Threading.Tasks.Task 任务) 第 66 行
[异步调用]
UI.exe !UI.Commands.AsyncCommand.ExecuteAsync(对象参数) 第 55 行
[异步调用]
UI.exe !UI.Commands.AsyncCommandBase.Execute(对象参数) 第 15 行

  1. 下一次中断会出现相同的异常,并且与之前的堆栈跟踪几乎相同(区别仅在于最后调用的 4 个方法中的第一个)。

调用堆栈

mscorlib.dll !System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(System.Threading.Tasks.Task 任务) 第 180 行
mscorlib.dll !System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task 任务) 第 170 行
mscorlib.dll !System.Runtime.CompilerServices.TaskAwaiter.GetResult() 第 125 行
UI.exe !UI.Commands.AsyncCommandFactory.Create(System.Threading.CancellationToken token) 第 27 行
[恢复异步方法]
...

  1. 下一个同样仅在一种方法上有所不同。

调用堆栈

mscorlib.dll !System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(System.Threading.Tasks.Task 任务) 第 180 行
mscorlib.dll !System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task 任务) 第 170 行
mscorlib.dll !System.Runtime.CompilerServices.TaskAwaiter.GetResult() 第 125 行
UI.exe !UI.NotifyTaskCompletion.WatchTaskAsync(System.Threading.Tasks.Task 任务) 第 66 行
[恢复异步方法]
...

  1. 上次中断后,不再引发异常,并且 Task.IsFaulted 设置为 true。现在,UI 使用此绑定成功显示异常消息:
void CurrentStep.Process(CancellationToken cancellationToken)
{
    throw new Exception();
}
Run Code Online (Sandbox Code Playgroud)

最终编辑。

要了解问题的背景和接受的答案,您需要查看以下文章:

Ste*_*ary 5

\n

我不想\xe2\x80\x99 不想将异常直接传播回主 UI 循环;我想捕获任何异常并设置属性,以便通过数据绑定完成错误处理。

\n
\n\n

在这种情况下,您真正​​需要的是一个仅启动异步操作的同步命令,其中异步操作使用(或您编写的某种类似类型)表示。分解这样的操作(分为同步“启动”和异步数据绑定)比尝试将所有操作集中在一起更容易(这也是可能的 - 只是代码不那么短或可重用) ):NotifyTaskCompletion

\n\n
// Represents the execution of the current step.\nNotifyTaskCompletion ProcessCurrentStepCommandExecution\n{\n  get { return _processCurrentStepCommandExecution; }\n  set { _processCurrentStepCommandExecution = value; PropertyChanged(); }\n}\n\n...\n\nvar cancellationToken = ...; // Wherever you get this from.\nProcessCurrentStepCommand = new DelegateCommand(() =>\n{\n  ProcessCurrentStepCommandExecution = new NotifyTaskCompletion(async () =>\n  {\n    await Task.Run(() => CurrentStep.Process(cancellationToken));\n\n    // I\'m assuming here you only want to move to the next step if there are no errors.\n    // Otherwise, this should be in a finally block.\n    CurrentStep = CurrentStep.NextStepViewModel;\n  });\n});\n
Run Code Online (Sandbox Code Playgroud)\n\n

编辑:

\n\n

我相信您可能会遇到该文章的示例代码中的错误(在 MSDNMag 决定将其全部删除之前,注释中曾经有更新的代码,我正在努力更新代码示例,这是一个令人惊讶的长过程)。如果任务同步完成(出现异常或成功),则会出现该错误;在这种情况下,NotifyTaskCompletion<T>.TaskCompleted将是null.

\n\n

要解决此问题,请更改构造函数NotifyTaskCompletion<T>

\n\n
{\n    Task = task;\n    if (!task.IsCompleted)\n        TaskCompletion = WatchTaskAsync(task);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

对此:

\n\n
{\n    Task = task;\n    TaskCompletion = WatchTaskAsync(task);\n}\n
Run Code Online (Sandbox Code Playgroud)\n