簡體   English   中英

任務 <T> 和C#中的TaskContinuationOptions澄清?

[英]Task<T> and TaskContinuationOptions Clarification in C#?

我有這個簡單的代碼:

var g=  Task.Factory.StartNew<int> (() => 8)
       .ContinueWith (ant =>{throw null;})
       .ContinueWith (a =>{ Console.WriteLine("OK");},TaskContinuationOptions.NotOnFaulted);

 try{
      Console.WriteLine("1");
      g.Wait();
      Console.WriteLine("2");
     }

catch (AggregateException  ex)
      {Console.WriteLine("catch"); }

輸出 :

1
catch
System.AggregateException: A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread.

msdn:

TaskContinuationOptions.NotOnFaulted

指定如果延續任務的前端拋出未處理的異常,則不應該對其進行調度。 此選項對多任務延續無效。

好 。

在此輸入圖像描述

並且沒關系 - 不顯示此行導致prev行DID拋出異常。

問題:

  • 我是否收到了AggregateException異常,因為我沒有檢查過Exception屬性?

  • 我是否必須始終檢查先前是否會拋出異常(在每一行?)? 我不能檢查每一行!它沒有任何意義,非常煩人

  • 是不是try catch塊應該吞下異常? (我認為所有異常都會導致等待方法....所以?)

我是否收到了AggregateException異常,因為我沒有檢查過Exception屬性?

不,你得到一個例外,因為任務g被TPL取消(因為,如msdn所述,如果antescendent任務拋出異常,則不會安排此任務)。

我們這里有3個任務:

  1. 原始任務(使用StartNew)
  2. 第一個繼續任務(拋出異常)
  3. 第二個繼續任務(打印正常)(這是您的代碼中的g任務)。

問題是, 只有當第二個任務成功完成時 ,才會要求TPL啟動3d任務。 這意味着如果不滿足此條件,TPL將完全取消您新創建的任務。

你有未被觀察到的任務異常,因為你有一個你從未觀察過的臨時任務(我的列表中的任務2)。 因為你從來沒有觀察過它的故障狀態,它會拋出終結器來告訴你它。

您可以通過在catch塊中打印任務的狀態來檢查:

catch (AggregateException ex)
{ 
    Console.WriteLine("catch");
    // Will print: Status in catch: Canceled
    Console.WriteLine("Status in catch: {0}", g.Status);
}

我是否必須始終檢查先前是否會拋出異常(在每一行?)? (我不能檢查每一行!它沒有任何意義,非常煩人)

是的,您應該觀察先前的任務異常以避免此問題:

static class TaskEx
{
    public static Task ObserverExceptions(this Task task)
    {
        task.ContinueWith(t => { var ignore = t.Exception; },
                            TaskContinuationOptions.OnlyOnFaulted);
        return task;
    }
}

然后使用它如下:

var g=  Task.Factory.StartNew<int> (() => 8)
       .ContinueWith (ant =>{throw null;})
       .ObserveExceptions()
       .ContinueWith (a =>{ Console.WriteLine("OK");});

 try{
      Console.WriteLine("1");
      g.Wait();
      Console.WriteLine("2");
     }

catch (AggregateException  ex)
      {Console.WriteLine("catch"); }

更新:為最后一顆子彈添加了解決方案

是不是try catch塊應該吞下異常? (我認為所有異常都會導致等待方法....所以?)

我們在項目中有一組擴展方法(稱為TransformWith ),可以解決這個特定問題,並獲得以下內容:

  1. 異常會冒泡到catch塊和
  2. 我們不會使用TaskUnobservedException崩潰應用程序

這里的用法

var g = Task.Factory.StartNew(() => 8)
       .ContinueWith(ant => { throw null; })
       // Using our extension method instead of simple ContinueWith
       .TransformWith(t => Console.WriteLine("OK"));

try
{
    Console.WriteLine("1");
    // Will fail with NullReferenceException (inside AggregateExcpetion)
    g.Wait();
    Console.WriteLine("2");
}

catch (AggregateException ex)
{
    // ex.InnerException is a NullReferenceException
    Console.WriteLine(ex.InnerException);
}

這是一個擴展方法:

static class TaskEx
{
    public static Task TransformWith(this Task future, Action<Task> continuation)
    {
        var tcs = new TaskCompletionSource<object>();
        future
            .ContinueWith(t =>
            {
                if (t.IsCanceled)
                {
                    tcs.SetCanceled();
                }
                else if (t.IsFaulted)
                {
                    tcs.SetException(t.Exception.InnerExceptions);
                }
                else
                {
                    try
                    {
                        continuation(future);
                        tcs.SetResult(null);
                    }
                    catch (Exception e)
                    {
                        tcs.SetException(e);
                    }
                }
            }, TaskContinuationOptions.ExecuteSynchronously);

        return tcs.Task;
    }    
}
  • 我是否收到了AggregateException異常,因為我沒有檢查過Exception屬性?

任務總是拋出AggregateException: http//msdn.microsoft.com/en-us/library/system.threading.tasks.task.exception.aspx

您可以使用以下方式獲取原始異常:

var myTask = Task.Factory.StartNew(() => { throw new NotImplementedException(); });
var myException = myTask.Exception.Flatten().InnerException as NotImplementedException;
  • 我是否必須始終檢查先前是否會拋出異常(在每一行?)? (我不能檢查每一行!它沒有任何意義,非常煩人)

是的,這很煩人,你應該為每個任務創建兩個延續來檢查異常:一個檢查是否有異常來處理異常,另一個繼續操作,如果沒有異常,請參閱TaskContinuationOptions.OnlyOnFaultedTaskContinuationOptions.OnlyOnRanToCompletion 如果需要,您甚至應該創建第三個延續來處理取消。

  • 是不是try catch塊應該吞下異常? (我認為所有異常都會導致等待方法....所以?)

不,它不會,異常不會在更高級別拋出,您應該在任務延續上使用TaskContinuationOptions.OnlyOnFaulted來檢查是否存在異常。 只能使用.net 4中不可用的async關鍵字,才能在調用者級別獲取任務異常

像這樣處理AggregateExceptions:

catch(AggregateException aex)
{
    aex.Handle(ex =>
    {
       // Do some handling and logging
        return true;
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM