多线程应用调试问题

时间:2015-11-09 07:43:05

标签: c# multithreading mono xamarin.ios xamarin.android

我有一个用Mono(Xamarin)开发的多线程.Net应用程序,有很多背景异步运行Tasks

public Task UpdateAsync()
{
    return Task.Run (() => {
        .....
    });
}

我的问题是,其中一个任务在某个随机点失败并崩溃并关闭应用程序而没有任何错误且没有断点触发器。由于有很多异步Tasks运行,因此我无法查明问题而且非常困难。

有没有办法找到问题的方法和行,或者甚至更好地突破?

编辑: 我也尝试按照下面的建议注册UnhandledException,但它仍然没有处理任何错误,应用程序只是关闭而没有任何痕迹

AppDomain.CurrentDomain.UnhandledException += (o, e) =>{ Debugger.Break(); }

EDIT2: 我终于找到了这个问题,感谢这里的所有帮助。是否有可能通过改变下面的代码来建议一种方法来防止这种情况(使调试器中断,而不是崩溃)?

    public Task StagedUpdateAsync() 
    {

        return Task.Run (() => {
             .
             .
             .
           InvokeOnMainThread (() => 
              {
                   // somehow here it was trying to use a null object
                   // and application crashed
              });
         });
     }

3 个答案:

答案 0 :(得分:1)

您可以尝试添加: -

 AppDomain.CurrentDomain.UnhandledException += (o,e) =>{ Debugger.Break();}

然后检查e以查看异常是什么,您应该能够打开线程窗口并切换到导致问题的线程,然后使用调用堆栈退回。

答案 1 :(得分:1)

首先,我想要注意,在直接询问Result属性或Wait* method或任何其他阻止方法之前,任务本身不会从内部代码中引发异常,所以搜索异常的最佳位置是代码的结果部分。

MSDN有一篇关于exception handling for the Tasks的完美文章,您应该通过它来选择自己的方式来处理异常。我将在这里重现文章中的主要观点,但建议您阅读整篇文章:

  1. try/catch阻止,最容易编写,但是如果你有很多任务,那么在你的代码中为它选择一个地方可能很有挑战性。请注意,您应该将AggregateException作为内部异常的包装器来捕获,如下所示:

    var task1 = Task.Run( () => { throw new CustomException("This exception is expected!"); } );
    
    try
    {
        task1.Wait();
    }
    catch (AggregateException ae)
    {
        // foreach here
    }
    
  2. 等待任务完成并检查它的状态:

    var task1 = Task.Run( () => { throw new CustomException("This exception is expected!"); } );
    
    while(! task1.IsCompleted) {}
    if (task1.Status == TaskStatus.Faulted)
    {
        // foreach here
    }
    
  3. 如果您的代码正在创建一些内部任务(attached or not),或者您正在创建一组任务,那么它们也可以引发异常,您应该检查{{的展平版本3}}:

    try {
        task1.Wait();
    }
    catch (AggregateException ae) {
        throw ae.Flatten();
    }
    
    try {
        Task.WaitAll(tasks.ToArray());
    }
    catch (AggregateException ae) {
        throw ae.Flatten();
    }
    
  4. 使用AggregateException过滤出现故障的内容(请注意,异常仍为tasks continuation个:

    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);
    
  5. 如果您仍然遗漏该异常,请使用AggregateException代表您正在使用的UnobservedTaskException event,类似于{{1}中您尝试处理的TaskScheduler (event args是UnobservedTaskExceptionEventArgs):

    AppDomain

答案 2 :(得分:1)

对我而言,这听起来非常像async void问题。阅读它here,它基本上说:

  

简而言之,调用异步void方法时抛出的异常的处理方式与等待Task的方式不同,并且会使进程崩溃。不是很棒的经历。

尤其不是很好的体验,因为您无法在调试器中捕获它。可能是你现在遇到的问题。所以我建议你去追捕你的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签名的代码库,这两种方法也可以很好地避免将来出现问题。 祝你好运!