多线程应用调试问题
Multithreaded app debug issue
我有一个用 Mono (Xamarin) 开发的多线程 .Net 应用程序,有很多后台异步-运行 Tasks
public Task UpdateAsync()
{
return Task.Run (() => {
.....
});
}
我的问题是其中一个任务在某个随机点失败并崩溃并关闭应用程序,没有任何错误,也没有断点触发器。我无法查明问题所在,这真的很难,因为有很多 运行 异步 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
});
});
}
你可以尝试添加这个:-
AppDomain.CurrentDomain.UnhandledException += (o,e) =>{ Debugger.Break();}
然后检查 e 以查看异常是什么,您应该能够打开线程 window 并切换到导致问题的线程,然后使用调用堆栈返回。
首先,我想指出的是,任务本身不会在其内部代码中引发异常,直到直接要求它们提供 Result
property or Wait*
method 或任何其他阻塞方法,所以这是完美的地方搜索异常是代码的结果部分。
MSDN 有一篇关于 exception handling for the Tasks 的完美文章,您应该通过它找到 select 您自己的异常处理方式。我会在这里重现文章的主要思想,但你建议你阅读整篇文章:
try/catch
块,最容易编写,但如果您有很多任务,在您的代码中 select 放置它的位置可能具有挑战性。请注意,您应该捕获 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();
}
使用 tasks continuation 过滤错误的(注意异常仍然是 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);
如果您仍然缺少异常,请使用 UnobservedTaskException
event for the TaskScheduler
you are using, similar to one you're trying to handle in AppDomain
(event args is an 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();
}
对我来说,这听起来很像 async void
问题。仔细阅读 here,它基本上说:
In short, exceptions thrown when calling an async void method isn't handled the same way as awaiting a Task and will crash the process. Not a great experience.
尤其不是很好的体验,因为您将无法在调试器中捕获它。可能是你现在遇到的问题。所以我建议你去寻找你的 async void
方法。现在的问题是 async void
方法很容易被发现
public async void Foo()
{
await Task.Run(() => {});
}
或者很好地隐藏在 lambda 后面
Action foo = async () => await Task.Run(() => {});
因此在更大的代码库中标记它们成为一项非常繁琐的任务。幸运的是,前面提到的 article 的作者提供了一个基于反射搜索 async void
签名的自动化解决方案。去看看吧。
如果您使用的是 Visual Studio 2015,您还可以使用基于 Roslyn 的代码分析器。 GitHub.
上有专门针对 async/await 的
这两种方法也很有效,可以通过定期检查代码库中的 async void
签名来避免将来出现问题。
祝你好运!
我有一个用 Mono (Xamarin) 开发的多线程 .Net 应用程序,有很多后台异步-运行 Tasks
public Task UpdateAsync()
{
return Task.Run (() => {
.....
});
}
我的问题是其中一个任务在某个随机点失败并崩溃并关闭应用程序,没有任何错误,也没有断点触发器。我无法查明问题所在,这真的很难,因为有很多 运行 异步 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
});
});
}
你可以尝试添加这个:-
AppDomain.CurrentDomain.UnhandledException += (o,e) =>{ Debugger.Break();}
然后检查 e 以查看异常是什么,您应该能够打开线程 window 并切换到导致问题的线程,然后使用调用堆栈返回。
首先,我想指出的是,任务本身不会在其内部代码中引发异常,直到直接要求它们提供 Result
property or Wait*
method 或任何其他阻塞方法,所以这是完美的地方搜索异常是代码的结果部分。
MSDN 有一篇关于 exception handling for the Tasks 的完美文章,您应该通过它找到 select 您自己的异常处理方式。我会在这里重现文章的主要思想,但你建议你阅读整篇文章:
try/catch
块,最容易编写,但如果您有很多任务,在您的代码中 select 放置它的位置可能具有挑战性。请注意,您应该捕获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(); }
使用 tasks continuation 过滤错误的(注意异常仍然是
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);
如果您仍然缺少异常,请使用
UnobservedTaskException
event for theTaskScheduler
you are using, similar to one you're trying to handle inAppDomain
(event args is anUnobservedTaskExceptionEventArgs
):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(); }
对我来说,这听起来很像 async void
问题。仔细阅读 here,它基本上说:
In short, exceptions thrown when calling an async void method isn't handled the same way as awaiting a Task and will crash the process. Not a great experience.
尤其不是很好的体验,因为您将无法在调试器中捕获它。可能是你现在遇到的问题。所以我建议你去寻找你的 async void
方法。现在的问题是 async void
方法很容易被发现
public async void Foo()
{
await Task.Run(() => {});
}
或者很好地隐藏在 lambda 后面
Action foo = async () => await Task.Run(() => {});
因此在更大的代码库中标记它们成为一项非常繁琐的任务。幸运的是,前面提到的 article 的作者提供了一个基于反射搜索 async void
签名的自动化解决方案。去看看吧。
如果您使用的是 Visual Studio 2015,您还可以使用基于 Roslyn 的代码分析器。 GitHub.
上有专门针对 async/await 的这两种方法也很有效,可以通过定期检查代码库中的 async void
签名来避免将来出现问题。
祝你好运!