1

首先,我不確定我是否在問一些愚蠢的事情,所以在開始之前道歉。創建一個分組幾個I/O任務的任務

我有節約使用一個SqlCommand異步在DB實體的集合的方法,並返回表示異步操作的任務,到目前爲止,它的作品好。基本上它會爲每個實體生成一個SQL命令和一堆參數,並將它們全部添加到一個SqlCommand實例中(參數被編號)

但是,如果我嘗試插入很多實體,那麼當參數計數達到2100 ,SQL操作失敗,因爲這是限制。然後,我想批量分割實體集合,並按順序執行它們,然後返回一個Task,直到所有子任務完成纔會完成。 (可能是最後一個)

每個孩子的任務,將獲得的Int32說有多少行已經改變,最後的任務必須返回所有這些的總和。所以所有的任務都是Task。如果一個失敗,所有這些都必須「回滾」,所以它們必須共享連接和事務對象。

此外,我想確保我正確使用Task和SqlCommand的I/O完成端口,並且沒有線程在SQL Server上的操作完成時等待/複製,因爲此「保存「操作是從ASP.NET MVC中的異步控制器調用的。

這裏的正確方法是什麼?

問候。

回答

1

用C#5(並假設基於任務的異步),這將是相當簡單:

async Task<IReadOnlyList<SomeType>> PerformUpdatesAsync(
    IEnumerable<AnotherType> data) 
{ 
    var result = new List<SomeType>(); 

    foreach (var batch in data.SplitIntoBatches(BatchSize)) 
     result.Add(await PerformUpdateAsync(batch)); 

    return result; 
} 

在預C#5是困難的,但可以使用隊列例如做模擬異步foreach

Task<SomeType[]> PerformUpdatesAsync(IEnumerable<AnotherType> data) 
{ 
    var batches = new Queue<IEnumerable<AnotherType>>(
     data.SplitIntoBatches(BatchSize)); 

    var result = new List<SomeType>(); 

    var tcs = new TaskCompletionSource<SomeType[]>(); 

    AsyncCallback onEndUpdate = null; 
    onEndUpdate = 
     ar => 
     { 
      result.Add(EndUpdate(ar)); 

      if (batches.Count == 0) 
       tcs.SetResult(result.ToArray()); 
      else 
       BeginUpdate(batches.Dequeue(), onEndUpdate, null); 
     }; 

    BeginUpdate(batches.Dequeue(), onEndUpdate, null); 

    return tcs.Task; 
} 

如果你不喜歡使用閉包這種方式,你可以通過創建一個單獨的類做同樣的事情,將持有的所有局部變量在其領域:批量處理和「遞歸」拉姆達。

有一個在框架沒有SplitIntoBatches(),所以你必須把它寫自己(除非你已經做了)。

+0

非常感謝!非常說教。 – vtortola 2012-04-27 13:52:04

0

這是一個很好的問題。這裏是一些代碼:

Task<int>[] batchTasks = ...;//start the batches here. each task should return the count of changed rows 
Task<int> sumTask = Task.Factory.ContinueWhenAll(batchTasks, _ => { 
return batchTasks.Sum(x => x.Result); 
}); 

這會給你一個無阻塞的個別批次的總和。

當您啓動一個批處理一定要使用異步SQL API,如BeginExecuteReader。然後,你將完全無阻塞。

+0

對不起,我做了一個錯誤,我想運行子任務的順序,而不是平行的,因爲我不能使用併發調用相同的連接。我編輯了這篇文章。基本上,我想返回將是鏈中最後一個任務幷包含總結果的任務。對於那個很抱歉。 – vtortola 2012-04-20 12:40:43

1

如果你要拆分插入,但連續運行它們,你並不需要使用任務爲每個呼叫。你只需要正常的順序代碼。

創建一個啓動任務並返回它的方法。在任務主體中,只需執行任何你想要的正常順序代碼即可。完成後,退出任務主體並完成單個任務。

+0

好吧,差不多。這樣,執行SQL操作的後臺線程就會被阻塞。爲了避免這種情況,您可以異步編寫代碼並使用TaskCompletionSource來創建和設置Task的結果。 – svick 2012-04-20 14:28:55

+0

@svick這是我不知道該怎麼做。按順序運行多個異步操作,並獲得累積結果... – vtortola 2012-04-20 15:47:14

相關問題