スレッドローカルストレージ
スレッドローカルストレージを使うことにより、スレッドごとに別々のデータを格納することができます。格納されたデータはそのスレッドからしか取得あるいは変更できません。
スレッドローカルストレージを使うには、まずThread.AllocateDataSlotまたはAllocateNamedDataSlotメソッドにより、データを格納する「データスロット」をすべてのスレッドに割り当てます。スロットにデータを格納するにはThread.SetDataメソッド、取得するにはThread.GetDataメソッドを使用します。
サンプル「tls_01」に、AllocateDataSlotを使った例を示します。
Class Class1 Private Shared slot As LocalDataStoreSlot 'エントリポイント Shared Sub Main() 'すべてのスレッドにデータスロットを割り当てる slot = System.Threading.Thread.AllocateDataSlot() 'メインスレッドのスロットの内容を設定 System.Threading.Thread.SetData(slot, "こんにちは") 'Threadオブジェクトを作成する Dim t As New System.Threading.Thread( _ New System.Threading.ThreadStart(AddressOf DoSomething)) 'スレッドを開始する t.Start() t.Join() 'メインスレッドのスロットの内容を取得 Dim str As String = _ CStr(System.Threading.Thread.GetData(slot)) Console.WriteLine(str) Console.ReadLine() End Sub Private Shared Sub DoSomething() '別スレッドのスロットの内容を設定 System.Threading.Thread.SetData(slot, "さようなら") '別スレッドのスロットの内容を取得 Dim str As String = _ CStr(System.Threading.Thread.GetData(slot)) Console.WriteLine(str) End Sub End Class
class Class1 { private static LocalDataStoreSlot slot; //エントリポイント static void Main() { //すべてのスレッドにデータスロットを割り当てる slot = System.Threading.Thread.AllocateDataSlot(); //メインスレッドのスロットの内容を設定 System.Threading.Thread.SetData(slot, "こんにちは"); //Threadオブジェクトを作成する System.Threading.Thread t = new System.Threading.Thread( new System.Threading.ThreadStart(DoSomething)); //スレッドを開始する t.Start(); t.Join(); //メインスレッドのスロットの内容を取得 string str = (string) System.Threading.Thread.GetData(slot); Console.WriteLine(str); Console.ReadLine(); } private static void DoSomething() { //別スレッドのスロットの内容を設定 System.Threading.Thread.SetData(slot, "さようなら"); //別スレッドのスロットの内容を取得 string str = (string) System.Threading.Thread.GetData(slot); Console.WriteLine(str); } }
このアプリケーションを実行すると、
さようなら こんにちは
と表示されます。
まずメインスレッドでデータスロットに文字列「こんにちは」と格納し、その後に別スレッドでデータスロットに「さようなら」と格納しています。それぞれのデータがスレッド別に格納されていることが分かります。
ThreadStaticAttribute
スレッドローカルストレージをより簡単に使用する方法は、ThreadStaticAttribute属性を使うことです。静的フィールドにThreadStaticAttribute属性をつけることにより、このフィールドはスレッドごとに別の値を設定、取得できるようになります。
ThreadStaticAttributeを使用した例を、サンプル「tls_02」に示します。
Class Class1 'スレッドごとに一意な静的フィールド <ThreadStatic()> _ Private Shared slotData As String 'エントリポイント Shared Sub Main() 'メインスレッドのslotDataの内容を設定 slotData = "こんにちは" 'Threadオブジェクトを作成する Dim t As New System.Threading.Thread( _ New System.Threading.ThreadStart(AddressOf DoSomething)) 'スレッドを開始する t.Start() t.Join() 'メインスレッドのslotDataの内容を取得 Console.WriteLine(slotData) Console.ReadLine() End Sub Private Shared Sub DoSomething() '別スレッドのslotDataの内容を設定 slotData = "さようなら" '別スレッドのslotDataの内容を取得 Console.WriteLine(slotData) End Sub End Class
class Class1 { //スレッドごとに一意な静的フィールド [ThreadStatic] private static string slotData; //エントリポイント static void Main() { //メインスレッドのslotDataの内容を設定 slotData = "こんにちは"; //Threadオブジェクトを作成する System.Threading.Thread t = new System.Threading.Thread( new System.Threading.ThreadStart(DoSomething)); //スレッドを開始する t.Start(); t.Join(); //メインスレッドのslotDataの内容を取得 Console.WriteLine(slotData); Console.ReadLine(); } private static void DoSomething() { //別スレッドのslotDataの内容を設定 slotData = "さようなら"; //別スレッドのslotDataの内容を取得 Console.WriteLine(slotData); } }
このアプリケーションを実行すると先と同じく、
さようなら こんにちは
と表示されます。
別スレッドでslotDataの値を「さようなら」としていますが、メインスレッドでslotDataを取得すると「こんにちは」のままとなっていることに注目してください。
シングルトンとダブルチェックロッキング
これらの問題はここで取り上げるべきものではないかもしれませんが、興味深い話題であるため、ごく簡単に触れておきます。
シングルトン(Singleton)デザインパターンとは、あるクラスのインスタンスがただ1つであることが保障され、そのインスタンスにグローバルなアクセスを設定するデザインパターンです。
.NET Frameworkでスレッドセーフなシングルトンを実装する方法は、MSDNライブラリの「C# でのシングルトンの実装」や「Exploring the Singleton Design Pattern」、「Implementing the Singleton Pattern in C#」が参考になります。つまり基本的には、
- 静的な初期化を行うか
- ダブルチェックロッキング(Double-Check Locking)を使うか
- ダブルチェックロッキングを使わずに同期を行うか
ということになります。結論を言いますと、.NETでは、静的な初期化を行う方法がベストです。
また、「volatile and MemoryBarrier()...」などでは、ダブルチェックロッキングでvolatileの代わりにThread.MemoryBarrierメソッドを使った方法が紹介されています。
最後に
今後、より高いレベルの勉強をしたいという方には、下の参考資料がお勧めです。中でも、参考資料1はかなりお勧めです。この記事を参考にしていることを明記している記事は見かけませんが、明らかにそうであると思われる日本語の記事はよく見ます。また、公開されているライブラリにも便利なクラスがあります。
この記事が読者の皆様のお役に立つことを祈っております。
参考資料
- Jon's Homepage! 『Multi-threading in .NET』
- MSDNライブラリ 『.NET Framework開発者ガイド-スレッド処理』
- MSDNライブラリ 『Visual Basic言語の概念-Visual Basic .NETにおけるマルチスレッド』
- MSDNライブラリ 『.NET Framework開発者ガイド-非同期呼び出しの組み込み』
- MSDNライブラリ 『コンポーネントのマルチスレッド』
- MSDNライブラリ 『スレッド処理のデザイン ガイドライン』
- MSDNライブラリ 『非同期プログラミングのガイドライン』
- MSDNライブラリ 『Chapter 4 - Architecture and Design Review of a .NET Application for Performance and Scalability』
