連載.NETマルチスレッド・プログラミング入門第4回 デッドロックの回避とスレッド間での同期制御高木 健一http://www.woodensoldier.info/ 2005/06/15 |
![]() |
|
|
|
マルチスレッドに関するそのほかの話題
マルチスレッド・プログラミングの基礎についての話題は以上であるが、よりマルチスレッドを活用するためのいくつかの話題を簡単に紹介しておこう。
■Mutex(ミューテックス)による排他制御
Mutexクラス(System.Threading名前空間)はlockステートメントと同じ排他制御を行うための手段である(「mutex」とは「相互排除」の意味)。Mutexオブジェクトを作成し、WaitOneメソッドでロックを取得し、ReleaseMutexメソッドでロックを解放することで排他制御を行う。
lockステートメントとの違いは、Mutexクラスではスレッド間だけではなくプロセス間での排他制御が行えることである。これによって、アプリケーションをまたがった排他制御を簡単に実現することができ、アプリケーションの多重起動のチェックなどによく使用される。次のList4は単純なMutexクラスによる排他制御のサンプルである。
|
|
| List4 Mutexクラスにより排他制御を行うC#のサンプル・プログラム | |
|
|
| List4 Mutexクラスにより排他制御を行うVB.NETのサンプル・プログラム | |
Mutexクラスを利用したサンプル・プログラムは「.NET TIPS:Windowsアプリケーションの多重起動を禁止するには?」でも紹介されているので参照していただきたい。
■Monitorクラス以外の同期制御の方法
今回では同期制御の手段としてMonitorクラスについて解説したが、これ以外にもイベントを用いる方法(AutoResetEventクラスやManualResetEventクラスを利用する方法)もSystem.Threading名前空間には用意されている。
■スレッドごとのデータ領域となるデータスロット
ThreadクラスにはAllocateDataSlot/AllocateNamedDataSlotという静的メソッドが用意されている。これらを利用することで、「データスロット」と呼ばれるスレッドごとにデータを格納しておく領域を確保することができる。スレッドごとに個別に保持したいデータなどがある場合に利用する価値があるだろう。
確保したデータスロットに対してはSetDataメソッドでデータの設定を、GetDataメソッドでデータの取得を行う。不要となったデータスロットはFreeNamedDataSlotメソッドで解放しておく必要がある。
■現在のスレッドを取得する
Threadクラスの静的メソッドであるCurrentThreadメソッドを使用することで、現在のコードがどのスレッドで実行されているのかを取得することができる。
Thread thread = Thread.CurrentThread;
■スレッドセーフなコレクション・クラス
ArrayListクラスやHashtableクラスなど.NET Frameworkのコレクション・オブジェクト(System.Collections名前空間のクラス)を複数のスレッドからアクセスするときには、「SyncRoot」というプロパティを用いてコレクションをスレッドセーフにすることができる。このプロパティは、データの先頭から順番にコレクションの中身の一覧を表示するときに、途中で要素の挿入などがないようにするためになどに使用される。
具体的なサンプル・コードなどは「.NET TIPS:スレッドセーフなコレクション・オブジェクトを作成するには?」を参照していただきたい。
■lockステートメントでロック対象とするオブジェクト
いくつかのサンプル・コードでは、よく「this」(VB.NETの場合には「Me」)や、typeof演算子(VB.NETではGetType演算子)を用いたロックの取得が頻繁に使用されているが、実際にはそういったコードはロック排他制御によるパフォーマンスの低下を引き起こしやすいプログラムになる。なぜなら、クラスのインスタンスやクラスのタイプ(Typeオブジェクト)のロックは、そのクラスのコード以外の場所でもできてしまうからである(無関係な処理の間で排他制御を行ってしまうことになる)。
もしクラスの外部で思いもかけずに長時間ロックを保持してしまうような処理があれば、その影響はクラスの内部にまで及んでしまう。このような状況を避けるために、スレッドセーフなクラスでロックを利用するときには、ロック専用のオブジェクトを作成し排他制御を行うとよい。
|
|
| パフォーマンスの低下を引き起こす可能性が大きいコード | |
| typeof演算子により得られるResourceクラスのTypeオブジェクトはほかのクラスなどからも取得可能であり、無用な排他制御が行われてしまう可能性がある。 |
|
|
| ロック専用のオブジェクトを用いたより安全なコード | |
| このようにしておけば、ロック対象となるオブジェクトが利用される範囲をクラス内に限定できる。 |
この記述方法は、本連載のサンプル・プログラムでもすでに何度か利用している。
マルチスレッド・プログラミングにおける注意点のまとめ
以上、全4回にわたり解説してきたが、最後にマルチスレッド・プログラミングを行うに当たっての留意点をまとめておこう。
-
マルチスレッド化を検討するときは、パフォーマンスの向上が得られるかどうかを考える
-
マルチスレッド・プログラミングでは、発見しにくいバグや考慮しなくてはならない点が多い
-
マルチスレッド・プログラミングの実装方法を、Threadクラス、ThreadPoolクラス、デリゲート、タイマーの中から適切に選択すること
-
スレッドでの処理が長時間にわたる可能性がある場合には、スレッドプールの使用は避けること
-
別スレッドでの処理を1つのクラス内で完結させることができるのであれば、外部からはマルチスレッドを意識させない構造にしておく(ただしドキュメントにはきちんと記述すること)
-
共通リソースとなるクラスは別クラスにし、スレッドセーフなコンポーネントとして排他制御などを行い、データをしっかりと守ること。サブクラス化もできないようにしておく
-
可能な限りロックの取得は行わなくてもよい設計にすること
-
ロックをしている時間と範囲はできる限り短くするように努力すること
-
InterlockクラスやReaderWriterLockクラスを使用することができないか検討すること
-
複数リソースのロックを同時に取得するようなコードはデッドロックの原因になるので避けること
-
スレッドプールによる処理の内部で、さらにスレッドプールを使用する処理を行わないこと
-
ロックはできる限りprivateなオブジェクトに対してかけること
あとがき
マルチスレッド・プログラミングの要求はこれからますます増していくと考えられる。特にCPUがデュアルコア化してくると、普通のPCが複数のCPUを持つことに等しく、マルチスレッド化によってパフォーマンスの向上が期待できる場面はますます多くなってくるだろう。
しかし、マルチスレッド・プログラミングにはシングルスレッド・プログラミングにはなかった注意すべき点が多い。本連載がマルチスレッド・プログラムを開発する指針になれば幸いである。![]()
| INDEX | ||
| .NETマルチスレッド・プログラミング入門 | ||
| 第4回 デッドロックの回避とスレッド間での同期制御 | ||
| 1.マルチスレッドで最も注意が必要な「デッドロック」 | ||
| 2.スレッド同士を協調動作させる「同期制御」 | ||
| 3.マルチスレッドに関するそのほかの話題とまとめ | ||
| 「.NETマルチスレッド・プログラミング入門」 |
TechTargetジャパン
- 新人プログラマーのためのInsider.NETの歩き方 2012 (2012/5/22)
晴れて.NETプログラマーとなる新人が効率的に開発技術を習得するには? 大量にある記事群の中から新人が読むべきお勧めを厳選して紹介 - jQuery MobileでJavaScriptプログラミング (2012/5/17)
jQuery Mobileは手軽なだけでなく、JavaScriptのAPIも充実しており、独自機能の実装もできる。今回は「グローバル設定」と「イベント」を解説 - Windows上で開発するための開発環境構築入門 (2012/5/16)
Windowsを使ってチームで開発している? なのにサーバOSを設定・運用した経験がない? そうなら、今すぐ学ぼう - 「コントラクト」でアプリのサンドボックスを乗り越える! (2012/5/11)
Metroスタイル・アプリはサンドボックスの中で動作する。それを乗り越えてほかのアプリと連携する仕組み「コントラクト」を解説
|
|
キャリアアップ
スポンサーからのお知らせ
- - PR -
イベントカレンダー
- - PR -


