2013-06-08 28 views
5

Tôi có đoạn mã sau:Tôi nên truyền bá ngoại lệ nhiệm vụ từ tác vụ tiếp tục trong .NET 4.0 như thế nào?

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(); 

đâu:

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

gì mã này làm là thực hiện câu lệnh SQL nào đó trên vô số các kết nối db, nơi một số các kết nối có thể thuộc về một máy chủ DB, trong khi những người khác - khác và như vậy.

Mã này đảm bảo rằng hai điều kiện tổ chức:

  1. SQL báo cáo đang chạy song song trên các máy chủ DB sẵn.
  2. Trong cùng một máy chủ DB, các câu lệnh SQL được chạy không đồng bộ, nhưng tuần tự.

Với kết nối N db mỗi một số máy chủ DB sẽ có một single Task trong phần cuối của tập hợp, thực hiện mà có tác dụng sau:

  • Execute SQL cho db conn 1
    • Sau khi hoàn thành trước đó, hãy thi hành SQL cho db conn 2
      • Sau khi hoàn thành trước đó, hãy thi hành SQL cho db conn 3
      • ...
        • Sau khi hoàn thành trước, thực hiện SQL cho db conn N

Vấn đề của tôi là ngay bây giờ ngoại lệ bị mất trừ của kết nối db đầu tiên. Tôi biết tôi nên kiểm tra các đối số _ và bằng cách nào đó xử lý các tài sản _.Exception bên trong chức năng tiếp tục. Tôi tự hỏi nếu có một cách thanh lịch để làm điều đó.

Bất kỳ ý tưởng nào?

+0

Vì vậy, nếu một ngoại lệ xảy ra đối với một số kết nối, bạn muốn bằng cách nào đó lưu nó, và sau đó tiếp tục với các kết nối sau, đúng? – svick

+0

Ngoài ra, tôi không nghĩ rằng nó có ý nghĩa nhiều khi sử dụng phương pháp không đồng bộ, nếu bạn sau đó sẽ đồng bộ 'Wait()' cho kết quả. – svick

Trả lời

1
  1. Nếu bạn định sử dụng phương pháp không đồng bộ, thì toàn bộ phương pháp phải không đồng bộ và không được chặn bất kỳ chủ đề nào.
  2. Nếu bạn không chặn, không có nhiều lý do để sử dụng PLINQ, việc thiết lập các tiếp tục từ một chuỗi đơn lẻ phải đủ nhanh.
  3. Nếu bạn muốn tiếp tục khi ngoại lệ xảy ra, bạn sẽ phải tự lưu trữ các ngoại lệ ở đâu đó.
  4. Tôi cảm thấy không thoải mái khi làm việc với một bộ sưu tập ngoại lệ mà không ném chúng, nhưng tôi đoán điều đó là ổn cho một hoạt động, có thể thất bại một phần và thành công một phần.

Cùng với đó, mã của tôi để làm điều này sẽ trông như thế này:

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

Tôi nghĩ về việc thu thập các ngoại lệ bằng tay, chỉ cần hy vọng. NET có thể có một số công cụ để làm điều đó thanh lịch hơn. Tôi muốn thực hiện song song, bởi vì các yêu cầu đi đến các máy chủ DB khác nhau. Trên cùng một máy chủ DB, tuy nhiên, song song là không có giá trị, vì nút cổ chai là đĩa IO anyway, mà không phải là một ứng cử viên tốt cho song song. – mark

+0

@mark Có thể bạn đã bỏ lỡ quan điểm của tôi: mã của tôi thiết lập sự tiếp tục và sau đó (không đồng bộ) chờ tất cả chúng hoàn tất. Điều này có nghĩa là các yêu cầu đến các máy chủ khác nhau * sẽ * thực hiện song song. – svick

+0

Ồ, tôi ngu ngốc. Tất nhiên bạn có quyền. Tôi có thể nhìn thấy nó bây giờ. – mark

Các vấn đề liên quan