如何避免"Unobserved Task"异常?



我已经对任务感到困惑,我不确定一种摆脱困境的好方法。

我有数据,我将从两个不同的API中异步获取。连接是可疑的,我对我可以等待多长时间有一个紧张的限制。所需的行为是"我想等待2秒钟才能获得两组数据,如果2秒到期,我将采取任何已完成的数据,即使那是什么都没有的。"

我用这样的笨拙的设置完成了此操作:(它已经在工作线程上运行,因此同步等待不是问题。)

Task<IData> firstTask = _firstDataSource.GetDataAsync()
Task<IData> secondTask = _secondDataSource.GetDataAsync()
var whenBothTasksFinish = Task.WhenAll(firstTask, secondTask);
var timeoutTask = Task.Delay(TimeSpan.FromSeconds(2));
Task.WhenAny(whenBothTasksFinish, timeoutTask).Wait()
bool timedOut = timeoutTask.IsCompleted
if (timedOut) {
    return newIData[0];
}
var sources = new List<IData>();
if (firstTask.IsCompleted && !firstTask.IsFaulted) {
    sources.Add(firstTask.Result);
} else if (firstTask.IsFaulted) {
    LogException(firstTask.Exception);
}
// Same block as above for secondTask

这很混乱,但是我写的时候很忙于时间。也许有一种更好的方法可以解决,今晚我会想到这一点。现在,我对如何解决这个问题感到困惑。

问题在关闭网络连接时发生,我的两个任务失败了。由于我记录了他们的例外,因此我满足了否则会导致未观察到的感受的条件。但是他们的例外也冒出了任务。whenall()和,大概是任务。最终将最终确定,我的应用程序不观察到Taskexception。奇怪的是,这似乎并没有始终如一地崩溃,但是有时有时,这就是我担心的原因。

我环顾四周,这似乎是我可以处理的一种方法是一个相当简单的延续:

whenBothTasksFinish.ContinueWith(t => LogException(t.Exception), TaskContinuationOptions.OnlyOnFaulted);

我这样做的唯一原因是,我喜欢当前的结构如何使我提供更个性化的日志消息:我关心哪个任务抛出了例外。我不想记录我得到的混乱的骨料感受,但并没有太多看它来解开它。

已经很晚了,我还没有花很多时间重新考虑这种算法。是否有一种方法可以确保我承认任务异常,或者有一种重组的方法来获得我想要的结果而不涉及这么多的活动部件?

我尝试了很多不同的事情,然后到达了此解决方案:

  whenBothTasksFinish.ContinueWith((parent) => {
    var flattened = parent.Exception.Flatten();
    flattened.Handle((ex) => true);
  }, TaskContinuationOptions.OnlyOnFaulted);

在正常情况下,这将是一个坏主意。它等效于空的try/catch。但是在我的情况下,我将稍后单独处理每个例外。这使得框架退出抱怨,我尚未处理WhenAll(中的异常。

奇数:当我在Xamarin Studio中进行调试时,持有一个断点过长的时间有时会导致WhenAll()任务在继续运行之前收集。我可能可以通过GC.KeepAlive()来处理这一点,但是Meh。

我有一些非常相似的解决方案,只要在任何人完全处理异步代码的情况下就将其留在此处。

当我尝试制作最低可重复的代码段时,这就是归功于:

using System.Threading.Tasks;
using System.Threading;
async Task ThrowsAsync() => throw new Exception();
Task DoesNotThrowAsync() => Task.CompletedTask;
// set a manual reset event when an unobserved task exception occurs
var mre = new ManualResetEventSlim();
TaskScheduler.UnobservedTaskException += (_, __) => mre.Set();
// minimum reproduced code
async Task StrangeBehaviorAsync() {
    var throwTask = ThrowsAsync();
    var noThrowTask = DoesNotThrowAsync();
    var whenAll = Task.WhenAll(throwTask, noThrowTask);
    // --- Some irrelevant code that befuddled the situation ---
    await throwTask;    // Naively just awaiting the task I cared about.  Causes unobserved task exception and exits the while loop below
    // await whenAll;   // No unobserved task exception, so while loop never exits
}

try
{
    await StrangeBehaviorAsync();
}
catch(Exception ex)
{
    Console.WriteLine(ex.Message);
}
// wait for unobserved task exception
while (!mre.IsSet)
{
    Console.WriteLine("No unobserved task exception...");   
    await Task.Delay(1000);
}

在.NET Interactive笔记本中运行此操作,上述代码将在whenAll最终确定时退出,这会导致未观察到的任务异常。删除await whenAll"观察"例外并防止重置事件设置,并且while循环永远运行。

因此,就像您的示例一样,我正在等待内在任务,但是有一个"何时"。尚未等待的任务(或您的情况,"续"),因此该框架将其视为未观察到的。

最新更新