2013-06-08 10 views
5

私は次のコードを持っている:、.NET 4.0の継続タスクからタスク例外を伝播する方法はありますか。

private static Exception GetTaskException(Task t) 
{ 
    try 
    { 
     t.Wait(); 
     return null; 
    } 
    catch (Exception exc) 
    { 
     return exc; 
    } 
} 

は何このコードはありませんが、いくつかの接続が1 DBサーバに属していてもよいDB接続の多数を通じて特定のSQL文を実行です:

innerExceptions = dbconnByServer 
    .AsParallel() 
    .WithDegreeOfParallelism(dbconnByServer.Count) 
    // A stream of groups of server connections proceeding in parallel per server 
    .Select(dbconns => dbconns.Select(dbconn => m_sqlUtilProvider.Get(dbconn))) 
    // A stream of groups of SqlUtil objects proceeding in parallel per server 
    .Select(sqlUtils => GetTaskException(sqlUtils 
     // Aggregate SqlUtil objects to form a single Task which runs the SQL asynchronously for the first SqlUtil, then upon completion 
     // for the next SqlUtil and so long until all the SqlUtil objects are processed asynchronously one after another. 
     .Aggregate<ISqlUtil, Task>(null, (res, sqlUtil) => 
     { 
      if (res == null) 
      { 
       return sqlUtil.ExecuteSqlAsync(SQL, parameters); 
      } 
      return res.ContinueWith(_ => sqlUtil.ExecuteSqlAsync(SQL, parameters)).Unwrap(); 
     }))) 
    .Where(e => e != null) 
    .ToList(); 

他のものは - 他のものなどに。 SQL文は、利用可能なDBサーバー間で並行して実行されている

  1. コードは、2つの条件が保持されていることを確認します。

  2. 同じDBサーバー内では、SQL文は非同期に実行されますが、順番に実行されます。

    • は、DB CONN 1
      • ためのSQLを実行し、いくつかのDBサーバあたりN DB接続は、以下の効果を有する実行、凝集の終わりに単一Taskが存在するであろう考える

前回の処理が完了したら、db conn 2のSQLを実行してください。

  • 前回の処理が完了したら、db conn 3のSQLを実行してください。
  • ...前回の完了時に
    • は、

私の問題は今、例外が非常に最初のDB接続のを除いて失われているということである デシベルでCONN NのためのSQLを実行します。私は_引数を調べて、何とか継続機能内の_.Exceptionプロパティを処理する必要があることを知っています。私はそれを行うためのエレガントな方法があるのだろうかと思います。

アイデア?

+0

例外はいくつかの接続のために発生した場合、あなたは何とかそれを保存したいし、次の接続を継続し、右? – svick

+0

また、非同期メソッドを使用するのは、結果を同期的に 'Wait()'するつもりではないと思います。 – svick

答えて

1
  1. 非同期メソッドを使用する場合は、メソッド全体が非同期である必要があり、スレッドをブロックしないでください。
  2. ブロックするつもりがない場合は、PLINQを使用する理由はあまりありません。単一のスレッドからの継続を設定するのは十分速いはずです。
  3. 例外が発生したときに続行したい場合は、自分でどこかに例外を格納する必要があります。
  4. 例外のコレクションを投げずに作業するのは気になりませんが、部分的に失敗し部分的に成功する操作は問題ありません。それと

、これを行うための私のコードは次のようになります。だから、

public Task<IEnumerable<Exception>> ExecuteOnServersAsync(
    IList<IEnumerable<Connection>> dbConnByServer, 
    string sql, object parameters) 
{ 
    var tasks = new List<Task>(); 
    var exceptions = new ConcurrentQueue<Exception>(); 

    Action<Task> handleException = t => 
    { 
     if (t.IsFaulted) 
      exceptions.Enqueue(t.Exception); 
    }; 

    foreach (var dbConns in dbConnByServer) 
    { 
     Task task = null; 

     foreach (var dbConn in dbConns) 
     { 
      var sqlUtil = m_sqlUtilProvider.Get(dbConn); 

      if (task == null) 
      { 
       task = sqlUtil.ExecuteSqlAsync(sql, parameters); 
      } 
      else 
      { 
       task = task.ContinueWith(
        t => 
        { 
         handleException(t); 
         return sqlUtil.ExecuteSqlAsync(sql, parameters); 
        }).Unwrap(); 
      } 
     } 

     if (task != null) 
     { 
      task = task.ContinueWith(handleException); 
      tasks.Add(task); 
     } 
    } 

    return Task.Factory.ContinueWhenAll(
     tasks.ToArray(), _ => exceptions.AsEnumerable()); 
} 
+0

私は手動で例外を収集することを考えましたが、.NETがもっと優雅にそれを行うためのいくつかのツールを持っていると思っていました。要求が異なるDBサーバーに送られるため、並列実行が必要です。しかし、同じDBサーバー上では、パラレルは価値がありません。なぜなら、ボトルネックはディスクIOなので、並列化の候補にはならないからです。 – mark

+0

@markおそらくあなたは私の意見を忘れていました。私のコードは継続を設定してから、(非同期に)すべてが完了するのを待っています。これは、異なるサーバー*への要求が並行して実行されることを意味します。 – svick

+0

ああ、愚かな私。もちろん、あなたは正しいです。私は今それを見ることができます。 – mark

関連する問題