.NetでConcurrentBagとConcurrentDictionaryを操作する方法
.Netの並行コレクションは、System.Collections.Concurrent名前空間内に含まれ、コレクションクラスのロックフリーでスレッドセーフな実装を提供します。スレッドセーフコレクションは.Net4で最初に導入され、コレクションは.Net Framework 1.0の一部として最初に導入され、System.Collections名前空間で利用可能になりました。
並行コレクションを利用してコレクションを操作できます。スレッド同期のために追加のコードを記述する必要はありません。ConcurrentStackとConcurrentQueueに関する私の記事をご覧ください。
ConcurrentBag
ConcurrentBagは、順序付けられていない要素のセットのスレッドセーフなコレクションを提供します。ConcurrentBagクラスの重要なメソッドのリストは次のとおりです。
- Add(T element)-このメソッドは、ConcurrentBagに要素を追加するために使用されます。
- TryPeek(out T)-このメソッドは、要素を削除せずにConcurrentBagから要素を取得するために使用されます。
- TryTake(out T)-このメソッドは、ConcurrentBagから要素を取得するために使用されます。このメソッドは、コレクションからアイテムを削除することに注意してください。
次のコードスニペットは、ConcurrentBagコレクションを作成してアイテムをそのコレクションに保存する方法を示しています。
ConcurrentBag concurrentBag = new ConcurrentBag();
for (int i = 0; i < 10; i++)
{
concurrentBag.Add(i);
}
コレクション内のアイテムを取得する場合は、次のコードを記述する必要があります。
while (concurrentBag.Count > 0)
{
Int32 element;
if (concurrentBag.TryTake(out element))
{
Console.WriteLine(element);
}
}
TryTakeメソッドがどのように使用されているかに注意してください。成功するとtrueを返し、それ以外の場合はfalseを返します。TryTakeメソッドは、コレクションからアイテムも削除します。whileループは、コレクション内のアイテムの数がゼロより大きくなるまで実行を継続します。参考までに、完全なコードリストを次に示します。
static void Main(string[] args)
{
ConcurrentBag concurrentBag = new ConcurrentBag();
for (int i = 0; i < 10; i++)
{
concurrentBag.Add(i);
}
while (concurrentBag.Count > 0)
{
Int32 element;
if (concurrentBag.TryTake(out element))
{
Console.WriteLine(element);
}
}
Console.Read();
}
ConcurrentDictionary
ディクショナリは、キーと値のペアの一般的なコレクションです。ボクシングとボックス化解除のオーバーヘッドがなくなるため、ハッシュテーブルよりも高速です。ConcurrentDictionaryは、System.Collections.Concurrent名前空間内に含まれ、スレッドセーフなディクショナリを表します。
ConcurrentDictionaryクラスの重要なメンバーは次のとおりです。
- TryAdd:このメソッドは、ConcurrentDictionaryインスタンスにアイテムを追加するために使用されます。キーがコレクションにすでに存在する場合、このメソッドは例外をスローすることに注意してください。
- TryGetValue:このメソッドは、コレクションからアイテムを取得するために使用されます。
- TryRemove:このメソッドは、コレクションからアイテムを削除するために使用されます。
- TryUpdate:このメソッドは、ConcurrentDictionaryインスタンスの特定のキーを指定された新しい値で更新するために使用されます。
次のコードスニペットは、ConcurrentDictionaryインスタンスを作成し、それにアイテムを追加する方法を示しています。
ConcurrentDictionary obj = new ConcurrentDictionary();
obj.TryAdd("X001", "This is the first value.");
obj.TryAdd("X002", "This is the second value.");
同じキーで別のアイテムを追加しようとすると、失敗します。以下のコードスニペットを参照してください。
bool success = obj.TryAdd("X002", "This is the third value.");
同じキーで値を追加しようとして失敗するため、success変数の値は「false」です。
次のコードスニペットは、キーに基づいてコレクションからアイテムを取得する方法を示しています。
string item = null;
bool isExist = obj.TryGetValue("X001", out item);
コレクション内のすべてのアイテムを取得する場合は、代わりに次のコードスニペットを使用できます。
foreach(var v in obj)
{
Console.WriteLine(v.Key + "---" + v.Value);
}
次のコードスニペットは、コレクションからアイテムを削除する方法を示しています。
string item = null;
bool result = obj.TryRemove("X001", out item);
すべてのアイテムを削除する場合は、代わりに次のコードスニペットを使用できます。
obj.Clear();
ここで、次の2つの静的メソッドについて考えます。
static void FirstTask(ConcurrentDictionary obj)
{
for (int i = 0; i < 10; ++i)
{
obj.TryAdd(i.ToString(), i.ToString());
Thread.Sleep(100);
}
}
static void SecondTask(ConcurrentDictionary obj)
{
Thread.Sleep(1000);
foreach (var item in obj)
{
Console.WriteLine("Key: "+item.Key + " Value: " + item.Value);
Thread.Sleep(100);
}
}
2つのタスクインスタンスで上記の2つのメソッドを同時に実行する方法は次のとおりです。1つはコレクションに値を格納し、もう1つはコレクションから値を読み取るためです。
ConcurrentDictionary obj = new ConcurrentDictionary();
Task firstTask = Task.Run(() => FirstTask(obj));
Task secondTask = Task.Run(() => SecondTask(obj));
try
{
Task.WaitAll(firstTask, secondTask);
}
catch (AggregateException ex)
{
//Write your own code here to handle exception
}
上記のコードを実行すると、ここのコレクションはスレッドセーフであるため、例外はスローされません。