【问题标题】:Looking for what caused the "A Task's exception(s) were not observed..."寻找导致“未观察到任务的异常...”的原因
【发布时间】:2017-07-27 14:45:14
【问题描述】:

首先,我要说的是,很难详细解释我的问题,但我会尽力而为。我将用我使用的可能导致异常的详细解释或更多代码进行更新。如果我的代码乱七八糟,我很抱歉。

我读过很多同名的SO问题,但我一点运气都没有。我对这里的Thread/Task/Dispatcher了解很少,如果发现我的代码有问题,请指导我。


简介

我的应用程序每 n 分钟通过计时器执行后台任务。

后台任务:从 API 获取数据,然后生成 Window 元素作为包含数据的表单,然后打印出来。

问题:该异常目前已经发生过两次,导致无法生成两个表单文档,因此无法打印。

从TaskScheduler.UnobservedTaskException 获取的详细异常是:

  • 在等待任务或访问其异常属性时未观察到任务的异常。结果,未观察到的异常被终结器线程重新抛出。

  • 堆栈跟踪:不适用
  • 内部异常:

    System.Collections.ObjectModel.ReadOnlyCollection`1[System.Exception]


这是我的一段代码,可能对您找到问题的根源有用:

public void BackgroundTask(object sender, EventArgs e)
{
    Application.Current.Dispatcher.Invoke(
        new Action(GetInvoiceData),
        DispatcherPriority.Background,
        null
    );
}

...GetInvoiceData 是:

public async void GetInvoiceData()
{
    try
    {
        JsonData = await ApiHelperInstance.Post(ApiParam);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
    finally
    {
        if (!string.IsNullOrEmpty(JsonData))
        {
            var apiReturn = new ApiReturn();

            try
            {
                apiReturn = JsonConvert.DeserializeObject<ApiReturn>(JsonData);
            }
            catch (JsonException ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                if (apiReturn.Result != null)
                {
                    foreach (ApiResult apiResult in apiReturn.Result)
                    {
                        InvoiceQueue.Enqueue(new Invoice(apiResult));
                    }

                    var worker = new BackgroundWorker();
                    worker.DoWork += GenerateDocumentAndPrint;
                    worker.RunWorkerAsync();
                }
            }
        }
    }
}

...而GenerateDocumentAndPrint 是:

public void GenerateDocumentAndPrint(object sender, DoWorkEventArgs e)
{
    while (InvoiceQueue.Count != 0)
    {
        Dispatcher.Invoke(() =>
        {
            Invoice invoice = InvoiceQueue.Dequeue();

            var invoiceForm = new InvoiceForm();
            var shippingLabelForm = new ShippingLabelForm();

            invoiceForm.Dispatcher.Invoke(async () =>
            {
                var invoiceTmp = invoice;
                var invoiceDoc = new FixedDocument();

                try
                {
                    invoiceDoc = await invoiceForm.CreateDocument(invoiceTmp);
                }
                finally
                {
                    InvoiceDocumentName = PrintJobNameSub + " - Invoice #" + invoice.TransOrder.TransNumber;
                    PrintHelperInstance.SetPrinterByName(InvoicePrinterName);
                    PrintHelperInstance.PrintDocument(invoiceDoc.DocumentPaginator, InvoiceDocumentName);
                    invoiceForm.Close();
                }
            }, DispatcherPriority.ContextIdle);

            shippingLabelForm.Dispatcher.Invoke(async () =>
            {
                var invoiceTmp = invoice;
                var shippingLabelDoc = new FixedDocument();

                try
                {
                    shippingLabelDoc = await shippingLabelForm.CreateDocument(invoiceTmp);
                }
                finally
                {
                    ShippingLabelDocumentName = PrintJobNameSub + " - Shipping Label #" + invoice.TransOrder.TransNumber;
                    PrintHelperInstance.SetPrinterByName(ShippingLabelPrinterName);
                    PrintHelperInstance.PrintDocument(shippingLabelDoc.DocumentPaginator, ShippingLabelDocumentName);
                    shippingLabelForm.Close();
                }
            }, DispatcherPriority.ContextIdle);
        }, DispatcherPriority.Normal);
    }
}

...来自InvoiceForm 和ShippingLabelForm 的异步方法CreateDocument 包含await Task.Delay(delay)。


我的代码有什么错误吗?是不是Dispatcher.Invoke用错了?是不是DispatcherPriority枚举的错误使用造成的? Task.Delay操作有问题吗?

【问题讨论】:

  • 如果发现 invoiceForm.CreateDocument(invoiceTmp) 抛出的异常怎么办?
  • @mm8 我已经捕获了所有未处理的异常,我提到的那个是唯一一个被捕获的。
  • 在调用 await invoiceForm.CreateDocument(invoiceTmp) 周围没有 catch 子句。
  • 最后。
  • 有理由以这种方式使用 Dispatcher 吗?为什么不是 TPL?

标签: c# wpf task dispatcher


【解决方案1】:

TaskScheduler.UnobservedTaskException 在未观察到任务异常时获取异常。如果你await你所有的任务,那么这个事件永远不会触发。

请注意,此事件并不一定意味着严格意义上的错误。例如,如果您放弃一项任务,这可能会发生 - 如果您的代码包含任何 Task.WhenAny 调用,这很常见。如果“即发即弃”任务引发异常,也会发生这种情况。在这两种情况下,它实际上都不是错误。在WhenAny 的情况下,另一个任务已经完成了Task.WhenAny,因此您不必关心另一个任务是否引发了异常。在“fire and forget”的情况下,“forget”的字面意思是“我不关心异常”,所以你不应该关心它是否抛出了异常。

此事件仅在您意外丢失await 时表示错误。查找丢失的await 的最简单方法是检查内部异常的调用堆栈,然后检查该方法的调用者等,直到找到不正确的await 执行任务。

【讨论】:

    猜你喜欢
    • 2016-11-25
    • 2013-11-26
    • 1970-01-01
    • 2012-11-06
    • 2011-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-01
    相关资源
    最近更新 更多