┏第26号━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃         .NETプログラミング研究         ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ 〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜メニュー ■.NET Tips ・.NETのマルチスレッドプログラミング その8 - ReaderWriterLockの使い方 - スレッドタイマの使い方 - スレッドセーフなコレクションを使う - Interlockedの使い方 〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜メニュー ─────────────────────────────── ■.NET Tips ─────────────────────────────── ●.NETのマルチスレッドプログラミング その8 .NETのマルチスレッドプログラミングについて、あれやこれやと書い てまいりましたが、主なところは一通り紹介しきったのではないかと 思いますので(それ以上にみなさん飽きたでしょう)、今回でひとま ず終わりにしたいと考えています。今回は今まで紹介していなかった 小ネタをできるだけ紹介するつもりですが、紹介し切れなかった事柄 については、後日DOBON.NET(http://dobon.net)で紹介する予定です。 ★ReaderWriterLockの使い方 ReaderWriterLockは、複数スレッドからの共有リソースへのアクセス を同期するために使用します。Monitorを使用しての同期と違い、 ReaderWriterLockは読み込みアクセスに対して複数のスレッドに許可 を与え、書き込みアクセスに対しては一つのスレッドにしか許可しま せん。よって、共有リソースからの読み込みが頻繁で、書き込みはめっ たになく、かつ短時間で終了するようなケースではReaderWriterLock は有効な手段となります。 スレッドが読み込みのためのロック(リーダーロック)を取得する時 は、ReaderWriterLock.AcquireReaderLockメソッドを、書き込みのた めのロック(ライタロック)を取得する時は、ReaderWriterLock. AcquireWriterLockメソッドを呼び出します。 以下にReaderWriterLockを使用した簡単な例を示します。このコード のReaderWriterLockを使っている箇所を削除すると同期が取れず、大 変なことになります。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ 'Imports System.Threading 'が宣言されているものとする 'ReaderWriterLockオブジェクトの作成 Private Shared rwl As New ReaderWriterLock '複数スレッドからアクセスする共通リソース Private Shared resource As String = "0123456789" 'エントリポイント Public Shared Sub Main() 'スレッドを作成し、開始する '作成するスレッドのうち半分は共有リソースから読み取るメソッドを 'もう半分は共有リソースに書き込むメソッドを実行する Dim i As Integer For i = 0 To 199 If i Mod 2 = 0 Then Dim t As New Thread( _ New ThreadStart(AddressOf ReadFromResource)) t.Start() Else Dim t As New Thread( _ New ThreadStart(AddressOf WriteToResource)) t.Start() End If Next i Console.ReadLine() End Sub '共有リソースから読み込む Private Shared Sub ReadFromResource() 'リーダーロックを取得 rwl.AcquireReaderLock(Timeout.Infinite) '共有リソースからの読み込みがスレッドセーフ Console.WriteLine(resource) 'ロックカウントをデクリメント '0になるとロックが解放される rwl.ReleaseReaderLock() End Sub '共有リソースに書き込む Private Shared Sub WriteToResource() 'ライタロックを取得 rwl.AcquireWriterLock(Timeout.Infinite) '共有リソースへの書き込み(読み込みも)がスレッドセーフ Dim s As String = resource.Substring(0, 1) resource = resource.Substring(1) Thread.Sleep(1) resource += s 'ライタロックカウントをデクリメント '0になるとロックが解放される rwl.ReleaseWriterLock() End Sub '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //using System.Threading; //が宣言されているものとする //ReaderWriterLockオブジェクトの作成 private static ReaderWriterLock rwl = new ReaderWriterLock(); //複数スレッドからアクセスする共通リソース private static string resource = "0123456789"; //エントリポイント public static void Main() { //スレッドを作成し、開始する //作成するスレッドのうち半分は共有リソースから読み取るメソッドを //もう半分は共有リソースに書き込むメソッドを実行する for (int i = 0; i < 200; i++) { if (i % 2 == 0) { (new Thread(new ThreadStart(ReadFromResource))).Start(); } else { (new Thread(new ThreadStart(WriteToResource))).Start(); } } Console.ReadLine(); } //共有リソースから読み込む private static void ReadFromResource() { //リーダーロックを取得 rwl.AcquireReaderLock(Timeout.Infinite); //共有リソースからの読み込みがスレッドセーフ Console.WriteLine(resource); //ロックカウントをデクリメント //0になるとロックが解放される rwl.ReleaseReaderLock(); } //共有リソースに書き込む private static void WriteToResource() { //ライタロックを取得 rwl.AcquireWriterLock(Timeout.Infinite); //共有リソースへの書き込み(読み込みも)がスレッドセーフ string s = resource.Substring(0, 1); resource = resource.Substring(1); Thread.Sleep(1); resource += s; //ライタロックカウントをデクリメント //0になるとロックが解放される rwl.ReleaseWriterLock(); } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ リーダーロックを取得中に共有リソースへ書き込む必要が生じたとき は、ReaderWriterLock.UpgradeToWriterLockメソッドを使ってライタ ロックへアップグレードすることができます。(ReaderWriterLock. UpgradeToWriterLockメソッドを呼び出したら、ReaderWriterLock. DowngradeFromWriterLockメソッドによりリーダーロックを復元します。) ReaderWriterLock.ReleaseLockメソッドでもロックを解放できますが、 この時はスレッドがロックを取得した回数に関係なく、すぐにロック を解放します。ReleaseLockメソッドが返すLockCookieオブジェクトを RestoreLockメソッドに使うことにより、解放したロックを復元するこ ともできます。このことを利用すれば、ロックを一時的に解放するこ とができます。 さらに、WriterSeqNumプロパティで現在のライタシーケンス番号を覚 えておき、それをAnyWritersSinceメソッドに使うことにより、その間 にライタロックを取得したスレッドがあるか調べることができます。 もしライタロックを取得したスレッドがあれば、共有リソースの内容 が変更されたと判断できます。これを利用すれば、例えば、リーダー ロック解放前に共有リソースの内容をローカル変数などに保存してお き、その後再びリーダーロックを取得してから共有リソースの内容が 変更されていれば共有リソースを読み込み、変更されていなければ保 存してある内容を使用するということができます。 次にUpgradeToWriterLock、DowngradeFromWriterLockメソッド、 ReleaseLockメソッド、RestoreLockメソッド、WriterSeqNumプロパテ ィ、AnyWritersSinceメソッドの具体的な使用例を示します。 UpAndDownGradeメソッドでUpgradeToWriterLock、 DowngradeFromWriterLockメソッドを、ReleaseAndRestoreメソッドで ReleaseLockメソッド、RestoreLockメソッド、WriterSeqNumプロパテ ィ、AnyWritersSinceメソッドを使っています。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ 'Imports System.Threading 'が宣言されているものとする 'ReaderWriterLockオブジェクトの作成 Private Shared rwl As New ReaderWriterLock '複数スレッドからアクセスする共通リソース Private Shared resource As String = "0123456789" 'エントリポイント Public Shared Sub Main() 'スレッドを作成し、開始する Dim i As Integer For i = 0 To 199 If i Mod 2 = 0 Then Dim t As New Thread( _ New ThreadStart(AddressOf UpAndDownGrade)) t.Start() Else Dim t As New Thread( _ New ThreadStart(AddressOf ReleaseAndRestore)) t.Start() End If Next i Console.ReadLine() End Sub '共有リソースを読み書きする Private Shared Sub UpAndDownGrade() 'リーダーロックを取得 rwl.AcquireReaderLock(Timeout.Infinite) '共有リソースからの読み取りがスレッドセーフ Console.WriteLine(resource) 'ライタロックにアップグレード Dim lc As LockCookie = rwl.UpgradeToWriterLock(Timeout.Infinite) '共有リソースへの書き込み(読み込みも)がスレッドセーフ Dim s As String = resource.Substring(0, 1) resource = resource.Substring(1) Thread.Sleep(1) resource += s 'リーダーロックに戻す rwl.DowngradeFromWriterLock(lc) 'ロックカウントをデクリメント '0になるとロックが解放される rwl.ReleaseReaderLock() End Sub 'ロックを解放し、復元する Private Shared Sub ReleaseAndRestore() 'リーダーロックを取得 rwl.AcquireReaderLock(Timeout.Infinite) '共有リソースからの読み取りがスレッドセーフ '共有リソースの内容を読み取る Dim resourceValue As String = resource '現在のライタシーケンス番号を覚えておく Dim seqNum As Integer = rwl.WriterSeqNum 'ロックを解放する '後で復元するため、LockCookieを記憶しておく Dim lc As LockCookie = rwl.ReleaseLock() 'ロックが解放されている 'ちょっと間を空ける Thread.Sleep(10) 'ロックを復元する rwl.RestoreLock(lc) 'ロック開放中に他のスレッドがライタロックを取得したか調べる 'trueならば共有リソースが変更されたとみなす If rwl.AnyWritersSince(seqNum) Then Console.WriteLine("({0})", resourceValue) '共有リソースから読み込む resourceValue = resource End If '表示する Console.WriteLine(resourceValue) 'ロックカウントをデクリメント '0になるとロックが解放される rwl.ReleaseReaderLock() End Sub '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //using System.Threading; //が宣言されているものとする //ReaderWriterLockオブジェクトの作成 private static ReaderWriterLock rwl = new ReaderWriterLock(); //複数スレッドからアクセスする共通リソース private static string resource = "0123456789"; //エントリポイント public static void Main() { //スレッドを作成し、開始する for (int i = 0; i < 200; i++) { if (i % 2 == 0) { (new Thread(new ThreadStart(UpAndDownGrade))).Start(); } else { (new Thread(new ThreadStart(ReleaseAndRestore))).Start(); } } Console.ReadLine(); } //共有リソースを読み書きする private static void UpAndDownGrade() { //リーダーロックを取得 rwl.AcquireReaderLock(Timeout.Infinite); //共有リソースからの読み取りがスレッドセーフ Console.WriteLine(resource); //ライタロックにアップグレード LockCookie lc = rwl.UpgradeToWriterLock(Timeout.Infinite); //共有リソースへの書き込み(読み込みも)がスレッドセーフ string s = resource.Substring(0, 1); resource = resource.Substring(1); Thread.Sleep(1); resource += s; //リーダーロックに戻す rwl.DowngradeFromWriterLock(ref lc); //ロックカウントをデクリメント //0になるとロックが解放される rwl.ReleaseReaderLock(); } //ロックを解放し、復元する private static void ReleaseAndRestore() { //リーダーロックを取得 rwl.AcquireReaderLock(Timeout.Infinite); //共有リソースからの読み取りがスレッドセーフ //共有リソースの内容を読み取る string resourceValue = resource; //現在のライタシーケンス番号を覚えておく int seqNum = rwl.WriterSeqNum; //ロックを解放する //後で復元するため、LockCookieを記憶しておく LockCookie lc = rwl.ReleaseLock(); //ロックが解放されている //ちょっと間を空ける Thread.Sleep(10); //ロックを復元する rwl.RestoreLock(ref lc); //ロック開放中に他のスレッドがライタロックを取得したか調べる //trueならば共有リソースが変更されたとみなす if (rwl.AnyWritersSince(seqNum)) { Console.WriteLine("({0})", resourceValue); //共有リソースから読み込む resourceValue = resource; } //表示する Console.WriteLine(resourceValue); //ロックカウントをデクリメント //0になるとロックが解放される rwl.ReleaseReaderLock(); } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ ★スレッドタイマの使い方 .NET Frameworkで用意されているタイマには、System.Windows.Forms. Timer(Windowsタイマ)、System.Threading.Timer(スレッドタイマ)、 System.Timers.Timer(サーバータイマ)の3種類があります。 Windowsフォームアプリケーションの開発ではWindowsタイマがよく使 われますが、これはOSのタイマ機能を使用しているため、スレッドで メッセージを処理しなければタイマは発生せず、長い処理によりブロ ックされる恐れがあります。これに対してサーバータイマとスレッド タイマはワーカースレッドにより処理されますので、その心配があり ません。 サーバータイマについてはヘルプによると、「サーバー ベースのタイ マは、サーバー環境での実行用に最適化された従来のタイマを強化し たものです。」とのことです。Visual Studio .NETのツールボックス の「コンポーネント」タブにあるのはこれです。 スレッドタイマは、システムが提供するスレッドプールを使用したタ イマです。イベントの代わりにコールバックメソッドを使用します。 軽量で、簡単なタイマ処理が必要な時に便利です。(これらのタイマ の違いに関する詳細は、ヘルプの「サーバー ベースのタイマの概説」 等をご覧ください。) ・サーバー ベースのタイマの概説 http://www.microsoft.com/japan/msdn/library/ja/vbcon/html/vbconserverbasedtimers.asp スレッドタイマThreading.Timerを作成するには、コンストラクタで実 行するメソッドへのTimerCallbackデリゲート、メソッドで使用される 状態オブジェクト、最初に発生する時間、発生する時間間隔を渡しま す。最初に発生する時間、発生する時間間隔を変更するには、Change メソッドを使います。タイマをキャンセルするには、Timer.Dispose関 数を呼び出します。なお、Dispose関数を呼び出せるように、 Threading.Timerオブジェクトへの参照は保持しておくべきです。 下のコードでは、TimerState.Tickメソッドをスレッドタイマにより、 定期的に実行します。まず0.1秒後に1秒間隔で実行し、TimerState. Tickメソッドが5回呼び出されると、0.5秒間隔で実行するように変更 します。そして10回呼び出されたところで、タイマを破棄します。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ 'Imports System.Threading 'が宣言されているものとする 'マルチスレッドメソッドのためのクラス 'using System.Threading; 'が宣言されているものとする Class TimerState Private Count As Integer = 0 Public ThreadTimer As System.Threading.Timer Public Sub Tick(ByVal state As Object) '状態オブジェクトの取得 Dim ts As TimerState = CType(state, TimerState) 'カウンタをインクリメント Count += 1 '表示 Console.WriteLine("システム起動後の経過時間:{0}ミリ秒({1})", _ System.Environment.TickCount, Count) If Count = 5 Then '5回呼び出されたとき、0.5秒おきに実行されるように変更する ts.ThreadTimer.Change(500, 500) Console.WriteLine("Changed") Else If Count = 10 Then '10回呼び出されたときは、タイマを破棄する ts.ThreadTimer.Dispose() ts.ThreadTimer = Nothing Console.WriteLine("Disposed") End If End If End Sub End Class Class MainClass 'エントリポイント Public Shared Sub Main() Dim ts As New TimerState 'Threading.Timerオブジェクトの作成 'TimerState.Tickを0.1秒後に1秒間隔で実行する ts.ThreadTimer = New System.Threading.Timer( _ New TimerCallback(AddressOf ts.Tick), ts, 100, 1000) '待機する Console.ReadLine() End Sub End Class '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //using System.Threading; //が宣言されているものとする class TimerState { private int Count = 0; public System.Threading.Timer ThreadTimer; public void Tick(object state) { //状態オブジェクトの取得 TimerState ts = (TimerState) state; //カウンタをインクリメント Count++; //表示 Console.WriteLine("システム起動後の経過時間:{0}ミリ秒({1})", System.Environment.TickCount, Count); if (Count == 5) { //5回呼び出されたとき、0.5秒おきに実行されるように変更する ts.ThreadTimer.Change(500, 500); Console.WriteLine("Changed"); } else if (Count == 10) { //10回呼び出されたときは、タイマを破棄する ts.ThreadTimer.Dispose(); ts.ThreadTimer = null; Console.WriteLine("Disposed"); } } } class MainClass { //エントリポイント public static void Main() { TimerState ts = new TimerState(); //Threading.Timerオブジェクトの作成 //TimerState.Tickを0.1秒後に1秒間隔で実行する ts.ThreadTimer = new System.Threading.Timer( new TimerCallback(ts.Tick), ts, 100, 1000); //待機する Console.ReadLine(); } } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 上記コードの結果は例えば次のようになります。 システム起動後の経過時間:8315236ミリ秒(1) システム起動後の経過時間:8316218ミリ秒(2) システム起動後の経過時間:8317219ミリ秒(3) システム起動後の経過時間:8318220ミリ秒(4) システム起動後の経過時間:8319222ミリ秒(5) Changed システム起動後の経過時間:8319723ミリ秒(6) システム起動後の経過時間:8320223ミリ秒(7) システム起動後の経過時間:8320724ミリ秒(8) システム起動後の経過時間:8321225ミリ秒(9) システム起動後の経過時間:8321726ミリ秒(10) Disposed 参考: ・タイマ http://www.microsoft.com/japan/msdn/library/ja/cpguide/html/cpcontimer.asp ・スレッド タイマ http://www.microsoft.com/japan/msdn/library/ja/vbcn7/html/vaconthreadtimers.asp ・Timer クラス http://www.microsoft.com/japan/msdn/library/ja/cpref/html/frlrfsystemthreadingtimerclasstopic.asp ★スレッドセーフなコレクションを使う System.Collections名前空間にあるコレクションクラス(ArrayList、 Queueなど)のインスタンスメンバは、スレッドセーフである保障があ りません。複数のスレッドがコレクションの内容を読み取る限りにお いては問題ありませんが、コレクションの内容が変更されうる場合は、 コレクションにアクセスするすべてのスレッドでその結果は保障され ません。(ただしHashtableクラスは単一の書き込み操作に関してはス レッドセーフが保障されています。) コレクションをスレッドセーフにするためには、コレクションの Synchronizedメソッドによりスレッドセーフラッパーを作成し、その ラッパーを通じてコレクションにアクセスするようにします。コレク ションがスレッドセーフであるか確かめるには、IsSynchronizedプロ パティがtrueかどうか調べます。 次にSynchronizedメソッドを使用した例を示します。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ 'using System.Collections; 'が宣言されているものとする '同期されていないArrayListオブジェクトの作成 Dim al As New ArrayList 'alを同期するArrayListラッパーの取得 Dim syncdAl As ArrayList = ArrayList.Synchronized(al) '同期されたArrayListオブジェクトの作成 Dim syncdAl2 As ArrayList = _ ArrayList.Synchronized(New ArrayList) '同期されているか確かめる Console.WriteLine("al.IsSynchronized = {0}", _ al.IsSynchronized) Console.WriteLine("syncdAl.IsSynchronized = {0}", _ syncdAl.IsSynchronized) Console.WriteLine("syncdAl2.IsSynchronized = {0}", _ syncdAl2.IsSynchronized) '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //using System.Collections; //が宣言されているものとする //同期されていないArrayListオブジェクトの作成 ArrayList al = new ArrayList(); //alを同期するArrayListラッパーの取得 ArrayList syncdAl = ArrayList.Synchronized(al); //同期されたArrayListオブジェクトの作成 ArrayList syncdAl2 = ArrayList.Synchronized(new ArrayList()); //同期されているか確かめる Console.WriteLine("al.IsSynchronized = {0}", al.IsSynchronized); Console.WriteLine("syncdAl.IsSynchronized = {0}", syncdAl.IsSynchronized); Console.WriteLine("syncdAl2.IsSynchronized = {0}", syncdAl2.IsSynchronized); //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 出力結果; al.IsSynchronized = False syncdAl.IsSynchronized = True syncdAl2.IsSynchronized = True コレクションの列挙はスレッドセーフな処理ではありませんので、コ レクションが同期されていても別のスレッドがコレクションの内容を 変更することがあり得るため、その結果例外をスローするかもしれま せん。列挙処理をスレッドセーフに行うには、列挙処理中コレクショ ンをlock(VB.NETではSyncLock)などによりロックします。 lockでコレクションをロックする時は、そのコレクション自身ではな く、コレクションのSyncRootプロパティを使用したほうがよいでしょ う。(スレッドセーフな独自のラッパーを作成する時にも、派生クラ スでSyncRootプロパティが使用されます。) 次にコレクションの列挙処理をスレッドセーフに行う例を示します。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ 'using System.Collections; 'が宣言されているものとする '同期されたArrayList Private Shared syncdAl As ArrayList = _ ArrayList.Synchronized(New ArrayList) 'エントリポイント Public Shared Sub Main() Dim i As Integer For i = 0 To 99 syncdAl.Add(i) Next i 'スレッドの作成と開始 Dim t As New Thread(New ThreadStart(AddressOf MyThread)) t.Start() 'ちょっと待ってから、syncdAlを変更する Thread.Sleep(500) syncdAl.RemoveAt(0) Console.ReadLine() End Sub Private Shared Sub MyThread() 'syncdAlをロックする 'これをしないと、例外がスローされる SyncLock syncdAl.SyncRoot '列挙処理をする Dim i As Integer For Each i In syncdAl Console.Write(i) Thread.Sleep(10) Next i End SyncLock End Sub '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //using System.Collections; //が宣言されているものとする //同期されたArrayList private static ArrayList syncdAl = ArrayList.Synchronized(new ArrayList()); //エントリポイント public static void Main() { for (int i = 0; i < 100; i++) { syncdAl.Add(i); } //スレッドの作成と開始 Thread t = new Thread(new ThreadStart(MyThread)); t.Start(); //ちょっと待ってから、syncdAlを変更する Thread.Sleep(500); syncdAl.RemoveAt(0); Console.ReadLine(); } private static void MyThread() { //syncdAlをロックする //これをしないと、例外がスローされる lock(syncdAl.SyncRoot) { //列挙処理をする foreach (int i in syncdAl) { Console.Write(i); Thread.Sleep(10); } } } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ ArrayクラスのようにSynchronizedメソッドがないコレクションでは、 上記と同じように、lockを使用することによりスレッドセーフにする ことができます。 参考: ・コレクションと同期 (スレッド セーフ) http://www.microsoft.com/japan/msdn/library/ja/cpguide/html/cpconcollectionssynchronizationthreadsafety.asp ★Interlockedの使い方 以前「スレッドの同期」にて、「競合状態」の説明をしました。競合 状態は「x++」のような単純な変数のインクリメントでも発生します。 一見一回の操作のみで行われるように見える変数のインクリメントも 実は、「変数の値を取得する → 1つ足す → 結果を変数に格納する」 という幾つかの操作により行われており、あるスレッドが変数に1足し て結果を格納する前に、別のスレッドが1足してしまうということが起 こりうるのです。 lockステートメントによる同期により競合状態を防いでインクリメン トを行うには、次のようにします。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ SyncLock Me x += 1 End SyncLock '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ lock (this) { x++; } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 実は.NET Frameworkではより優れた方法として、Interlocked. Incrementメソッドが用意されています。Interlocked.Incrementメソ ッドは、先に説明した変数インクリメントの一連の操作を、分割不可 能な操作として一度に行ってくれるため、競合状態の発生する隙を与 えません。(このように操作が分割不可能であることを「アトミック」 と呼びます。) 上記のコードをInterlocked.Incrementメソッドで書き直すと、次のよ うになります。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ System.Threading.Interlocked.Increment(x) '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ System.Threading.Interlocked.Increment(x); //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ Interlockedクラスの4つの静的メソッドIncrement、Decrement、 Exchange、CompareExchangeメソッドを使うことにより、整数のインク リメント、デクリメント、さらに変数への値の設定(変数の値の交換)、 変数の比較と値の設定を分割不可能な操作として実行できます。 参考: ・Interlocked http://www.microsoft.com/japan/msdn/library/ja/cpguide/html/cpconinterlocked.asp ・Interlocked クラス http://www.microsoft.com/japan/msdn/library/ja/cpref/html/frlrfSystemThreadingInterlockedClassTopic.asp ・スレッド処理のデザイン ガイドライン http://www.microsoft.com/japan/msdn/library/ja/cpgenref/html/cpconthreadingdesignguidelines.asp =============================== ■このマガジンの購読、購読中止、バックナンバー、説明に関しては  次のページをご覧ください。  http://www.mag2.com/m/0000104516.htm ■発行人・編集人:どぼん!  (Microsoft MVP for Visual Basic, Oct 2003-Oct 2004)  http://dobon.net  dobon_info@yahoo.co.jp ■ご質問等はメールではなく、掲示板へお願いいたします。  http://dobon.net/vb/bbs.html ■上記メールアドレスへのメールは確実に読まれる保障はありません  (スパム、ウィルス対策です)。メールは下記URLのフォームメール  から送信してください。  http://dobon.net/mail.html Copyright (c) 2003 - 2004 DOBON! All rights reserved. ===============================