C#のDictionaryはマルチスレッドに弱く、Add()をマルチスレッドで繰り返すと5万件程度で「IndexOutOfRangeException」が発生します。
List<T>ならマルチスレッドでAdd()を繰り返しても、1500万件程度まで「OutOfMemoryException」は発生しませんでした。
DictionaryのAdd()をlockで囲み、その部分だけシングルスレッドにすれば解消されますが、.Net2.0以上にはList<T>という、マルチスレッドに強く使い勝手も良い機能があるので、初めからDictionaryを使わずにList<T>で統一するのが望ましいです。
※.Net Core だと List<T> のAdd()もlockで囲んだ方が良さそう。
以下は実験した結果。
実験に使用したソース => Src実証実験をダウンロード
実証実験1(シングルスレッド)
DictionaryをシングルスレッドでAdd()し、「Key、Value」の文字数を横方向に爆発させた場合、「OutOfMemoryException」になる。
a、コード
1 2 3 4 5 6 7 8 9 10 11 |
Dictionary<string, string> dict = new Dictionary<string, string>(); string Key = ""; string Value = ""; while (true) { Key += "a"; Value += "b"; dict.Add(Key, Value); } |
b、結果
実証実験2(シングルスレッド)
DictionaryをシングルスレッドでAdd()し、「Key、Value」の要素数を縦方向に爆発させた場合、1000万件程度で「OutOfMemoryException」になる。
a、コード
1 2 3 4 5 6 7 8 9 10 11 |
Dictionary<string, string> dict = new Dictionary<string, string>(); long Key = 0; long Value = 0; while (true) { dict.Add(Key.ToString(), Value.ToString()); Key++; Value++; } |
b、結果
実証実験3(マルチスレッド)
DictionaryをマルチスレッドでAdd()し、「Key、Value」の要素数を縦方向に爆発させた場合、5万件程度で「IndexOutOfRangeException」になる。
a、コード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public class C実験3 { Dictionary<string, string> dict = new Dictionary<string, string>(); public void DictionaryAdd(object id) { long Key = 0; long Value = 0; while (true) { dict.Add((string)id + Key.ToString(), Value.ToString()); Key++; Value++; } } } private void 実証実験3() { var 実験3 = new C実験3(); var t1 = new Thread(new ParameterizedThreadStart(実験3.DictionaryAdd)); var t2 = new Thread(new ParameterizedThreadStart(実験3.DictionaryAdd)); var t3 = new Thread(new ParameterizedThreadStart(実験3.DictionaryAdd)); t1.Start("A"); t2.Start("B"); t3.Start("C"); } |
b、結果
実証実験4(マルチスレッド lock有り)
DictionaryをマルチスレッドでAdd()する際、Add()だけlockさせ、「Key、Value」の要素数を縦方向に爆発させた場合、1000万件程度で「OutOfMemoryException」になる。
a、コード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
public class C実験4 { Dictionary<string, string> dict = new Dictionary<string, string>(); public void DictionaryAdd(object id) { long Key = 0; long Value = 0; while (true) { lock (dict) { dict.Add((string)id + Key.ToString(), Value.ToString()); } Key++; Value++; } } } private void 実証実験4() { var 実験4 = new C実験4(); var t1 = new Thread(new ParameterizedThreadStart(実験4.DictionaryAdd)); var t2 = new Thread(new ParameterizedThreadStart(実験4.DictionaryAdd)); var t3 = new Thread(new ParameterizedThreadStart(実験4.DictionaryAdd)); t1.Start("A"); t2.Start("B"); t3.Start("C"); } |
b、結果
実証実験5(List<T> マルチスレッド)
List<T>をマルチスレッドでAdd()し、「Key、Value」の要素数を縦方向に爆発させた場合、1500万件程度で「OutOfMemoryException」になる。
a、コード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
public class C実験5 { private class StrData { public string str1; public string str2; } List dict = new List(); public void DictionaryAdd(object ThreadId) { long Key = 0; long Value = 0; while (true) { dict.Add(new StrData { str1 = (string)ThreadId + Key.ToString(), str2 = Value.ToString() }); Key++; Value++; } } } private void 実証実験5() { var 実験5 = new C実験5(); var t1 = new Thread(new ParameterizedThreadStart(実験5.DictionaryAdd)); var t2 = new Thread(new ParameterizedThreadStart(実験5.DictionaryAdd)); var t3 = new Thread(new ParameterizedThreadStart(実験5.DictionaryAdd)); t1.Start("A"); t2.Start("B"); t3.Start("C"); } |
b、結果
コメント