コレクションを分割してマルチスレッド処理を実行する
foreachのループ処理でコレクション内データを取得し、ループ内でシングルスレッド処理を行うことがあるかと思います。
foreach内でや大量データのバッチ処理や重い処理(APIによるCRUD処理など)を行う場合に、
予めコレクションを分割し、マルチスレッド処理にする方法を記載します。
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace CollectionSplit { class Program { static void Main(string[] args) { //0~11000までの連番をListに追加 var allList = Enumerable.Range(0, 11000).ToList(); var splitLists = GetSplitList(allList).Select(i => i.ToList()).ToList(); List<Task> taskList = new List<Task>(); //分割したコレクションをループし、マルチスレッド処理を実行 foreach (var splitList in splitLists.Select((item, index) => new { item, index })) { taskList.Add(Task.Factory.StartNew(() => { foreach (var splitItem in splitList.item) { //実際はここで重い処理(APIによるCRUD処理など)を実行 Console.WriteLine($"index: {splitList.index}, value: {splitItem}"); } }, TaskCreationOptions.LongRunning)); } Task.WaitAll(taskList.ToArray()); Console.ReadLine(); } /// <summary> /// コレクション分割メソッド /// </summary> /// <typeparam name="T"></typeparam> /// <param name="allList"></param> /// <returns></returns> private static IEnumerable<IEnumerable<T>> GetSplitList<T>(List<T> allList) { //コレクションを分割する単位を指定(この例では16分割する) int parallelCount = 16; var cntPerTask = allList.Count < parallelCount ? allList.Count : allList.Count / parallelCount; var cntMod = allList.Count % parallelCount; int count = 0; for (int i = 0; i < parallelCount; i++) { if (i != parallelCount - 1) { yield return allList.Skip(count).Take(cntPerTask); count += cntPerTask; } else { //余りは全て最後のコレクションに追加 if (cntMod != 0) cntPerTask += cntMod; yield return allList.Skip(count).Take(cntPerTask); } } } } }
参考サイト
ufcpp.net
実行結果