多线程应用程序调试问题
本文关键字:问题 调试 应用程序 多线程 | 更新日期: 2023-09-27 18:21:06
我有一个用Mono(Xamarin)开发的多线程.Net应用程序,它有很多后台异步运行Tasks
public Task UpdateAsync()
{
return Task.Run (() => {
.....
});
}
我的问题是,其中一个Task在某个随机点失败,并崩溃并关闭应用程序,而不会出现任何错误,也不会触发断点。我一直无法确定这个问题,因为有很多异步Tasks
运行,所以这真的很难。
有没有办法找到问题所在的方法和行,或者在这一点上更好地解决?
编辑:我也试着按照下面的建议注册UnhandledException
,但它仍然没有处理任何错误,应用程序只是关闭了,没有任何跟踪
AppDomain.CurrentDomain.UnhandledException += (o, e) =>{ Debugger.Break(); }
第2版:多亏了这里的所有帮助,我终于找到了问题。有没有可能通过修改下面的代码来建议一种防止这种情况的方法(使调试器中断,而不是应用程序崩溃)?
public Task StagedUpdateAsync()
{
return Task.Run (() => {
.
.
.
InvokeOnMainThread (() =>
{
// somehow here it was trying to use a null object
// and application crashed
});
});
}
首先,我想注意的是,任务本身不会从其内部代码中引发异常,直到直接要求它们提供Result
属性或Wait*
方法或任何其他阻塞方法,所以搜索异常的最佳位置是代码的结果部分。
MSDN有一篇关于任务异常处理的完美文章,您应该仔细阅读它,选择自己处理异常的方式。我将在这里复制文章的主要观点,但您建议您阅读整篇文章:
-
try/catch
块,最容易编写,但如果您有很多任务,那么在代码中为其选择位置可能会很有挑战性。注意,您应该捕获AggregateException
作为内部异常的包装,如下所示:var task1 = Task.Run( () => { throw new CustomException("This exception is expected!"); } ); try { task1.Wait(); } catch (AggregateException ae) { // foreach here }
-
等待任务完成并检查其状态:
var task1 = Task.Run( () => { throw new CustomException("This exception is expected!"); } ); while(! task1.IsCompleted) {} if (task1.Status == TaskStatus.Faulted) { // foreach here }
-
如果您的代码正在创建一些内部任务(无论是否附加),或者您正在创建一组任务,它们也可能引发异常,您应该检查
AggregateException
:的扁平版本try { task1.Wait(); } catch (AggregateException ae) { throw ae.Flatten(); } try { Task.WaitAll(tasks.ToArray()); } catch (AggregateException ae) { throw ae.Flatten(); }
-
使用任务继续过滤故障的任务(注意,异常仍然是
AggregateException
异常:var task1 = Task.Run(() => { throw new CustomException("task1 faulted."); }).ContinueWith(t => { Console.WriteLine("{0}: {1}", t.Exception.InnerException.GetType().Name, t.Exception.InnerException.Message); }, TaskContinuationOptions.OnlyOnFaulted);
-
如果您仍然缺少异常,请为您正在使用的
TaskScheduler
使用UnobservedTaskException
事件,类似于您试图在AppDomain
中处理的事件(事件参数是UnobservedTaskExceptionEventArgs
):TaskScheduler.Default.UnobservedTaskException += (o, e) => { Console.WriteLine(e.Exception.ToString()); Debugger.Break(); } // or TaskScheduler.Current.UnobservedTaskException += (o, e) => { Console.WriteLine(e.Exception.ToString()); Debugger.Break(); }
您可以尝试添加以下内容:-
AppDomain.CurrentDomain.UnhandledException += (o,e) =>{ Debugger.Break();}
然后检查e以查看异常是什么,您应该能够打开线程窗口并切换到导致问题的线程,然后使用调用堆栈后退一步。
对我来说,这听起来很像async void
问题。在这里阅读,它基本上说:
简而言之,调用异步void方法时抛出的异常与等待Task的处理方式不同,会导致进程崩溃。这不是一次很棒的经历。
特别是这不是一个很好的体验,因为你将无法在调试器中捕捉到它。可能是你现在遇到的问题。所以我建议你去寻找你的async void
方法。现在的问题是async void
方法可以明显地发现
public async void Foo()
{
await Task.Run(() => {});
}
或者隐藏在lambda 后面
Action foo = async () => await Task.Run(() => {});
因此,在更大的代码库中标记它们将成为一项非常乏味的任务。幸运的是,前面提到的文章的作者提供了一个基于反射搜索async void
签名的自动化解决方案。去看看吧。
如果您使用的是Visual Studio 2015,您也可以使用基于Roslyn的代码分析器。GitHub上有一个专门针对async/await的。
这两种方法也能很好地工作,以便通过定期检查async void
签名的代码库来避免将来的问题。祝你好运