C#でListDictionaryへの同期アクセスを取得するには?SyncRootとlockの使い方を解説
C#のListDictionaryに対して同期(スレッドセーフ)なアクセスを行うには、SyncRootプロパティをlockステートメントでロックします。これにより、複数のスレッドから同時にコレクションへアクセスしても、列挙中にコレクションが変更されることによる不整合や例外を防ぐことができます。
なお、ListDictionaryは既定では同期されておらず、IsSynchronizedプロパティはfalseを返します。そのため、マルチスレッド環境で安全に操作したい場合は、必ずlock(dict.SyncRoot)で処理を保護する必要があります。
コード例1:基本となる同期アクセスの実装
using System;
using System.Collections;
using System.Collections.Specialized;
public class Demo {
public static void Main() {
ListDictionary dict = new ListDictionary();
dict.Add("1", "SUV");
dict.Add("2", "Sedan");
dict.Add("3", "Utility Vehicle");
dict.Add("4", "Compact Car");
dict.Add("5", "SUV");
dict.Add("6", "Sedan");
dict.Add("7", "Utility Vehicle");
dict.Add("8", "Compact Car");
dict.Add("9", "Crossover");
dict.Add("10", "Electric Car");
Console.WriteLine("ListDictionary elements...");
foreach(DictionaryEntry d in dict) {
Console.WriteLine(d.Key + " " + d.Value);
}
Console.WriteLine("\nIs the ListDictionary having fixed size? = "+dict.IsFixedSize);
Console.WriteLine("If ListDictionary read-only? = "+dict.IsReadOnly);
Console.WriteLine("Is ListDictionary synchronized = "+dict.IsSynchronized);
Console.WriteLine("The ListDictionary has the key K? = "+dict.Contains("K"));
Console.WriteLine("The ListDictionary has the key 9? = "+dict.Contains("9"));
Console.WriteLine("\nSynchronize access...");
// SyncRootをロックして同期アクセスを実現
lock(dict.SyncRoot) {
foreach(DictionaryEntry d in dict) {
Console.WriteLine(d.Key + " " + d.Value);
}
}
}
}
出力結果
上記のコードを実行すると、次のような出力が得られます。
ListDictionary elements... 1 SUV 2 Sedan 3 Utility Vehicle 4 Compact Car 5 SUV 6 Sedan 7 Utility Vehicle 8 Compact Car 9 Crossover 10 Electric Car Is the ListDictionary having fixed size? = False If ListDictionary read-only? = False Is ListDictionary synchronized = False The ListDictionary has the key K? = False The ListDictionary has the key 9? = True Synchronize access... 1 SUV 2 Sedan 3 Utility Vehicle 4 Compact Car 5 SUV 6 Sedan 7 Utility Vehicle 8 Compact Car 9 Crossover 10 Electric Car
出力のポイント
IsFixedSizeおよびIsReadOnlyはどちらもFalseであり、要素の追加・削除が可能です。IsSynchronizedがFalseであることから、このコレクションがデフォルトではスレッドセーフではないことが分かります。Contains()メソッドにより、指定したキー("K"は存在せずFalse、"9"は存在しTrue)の有無を確認できます。
コード例2:lockブロックのみを使ったシンプルな例
続いて、lock(dict.SyncRoot)だけに焦点を絞ったシンプルな例を見てみましょう。
using System;
using System.Collections;
using System.Collections.Specialized;
public class Demo {
public static void Main() {
ListDictionary dict = new ListDictionary();
dict.Add("A", "Books");
dict.Add("B", "Electronics");
dict.Add("C", "Appliances");
dict.Add("D", "Pet Supplies");
dict.Add("E", "Clothing");
dict.Add("F", "Footwear");
Console.WriteLine("Synchronize access...");
// SyncRootでロックし、安全に列挙する
lock(dict.SyncRoot) {
foreach(DictionaryEntry d in dict) {
Console.WriteLine(d.Key + " " + d.Value);
}
}
}
}
出力結果
このコードを実行すると、次の出力が得られます。
Synchronize access... A Books B Electronics C Appliances D Pet Supplies E Clothing F Footwear
まとめ
ListDictionaryをマルチスレッド環境で安全に扱うには、SyncRootプロパティをlockステートメントで囲むのが基本的な手法です。読み取り・書き込み・列挙など、コレクションに触れるすべての処理を同じSyncRootでロックすることで、競合状態(レースコンディション)を防ぎ、一貫性のある動作を保証できます。
-
C#でタプル(Tuple)の3番目の要素を取得する方法
C#のタプル(Tuple)から3番目の要素を取得するには、Item3プロパティを使用します。タプルの各要素は、作成時の順序に応じて Item1、Item2、Item3… といった名前のプロパティに格納されるため、目的の位置に対応するプロパティを参照するだけで、簡単に値を取り出すことができます。例1:7要素のタプルで各要素を取得する次のコードでは、Tuple.Createメソッドで7つの整数を持つタプルを2つ生成し、Equalsメソッドによる等価比較、GetHashCodeメソッドによるハッシュコードの取得、さらに Item1 から Item7 までの各プロパティを使った要素の取得を行っています
-
C#でタプルの6番目の要素を取得する方法【Item6プロパティの使い方】
C#のタプル(Tuple)から6番目の要素を取得するには、Item6プロパティを使用します。System名前空間のTupleクラスは、Item1からItem7までの読み取り専用プロパティを持っており、それぞれ対応する位置の要素にアクセスできます。 例1:Item6プロパティで6番目の要素を取得する 次のコードでは、Tuple.Createメソッドで7つの要素を持つタプルを2つ作成し、Equalsメソッドによる等価性の確認、GetHashCodeメソッドによるハッシュコードの取得を行った後、Item6プロパティで6番目の要素を取り出しています。 using System; public clas