オブジェクトのロック
オブジェクトクラスは、それ自身をロックすることができる。下記のように lock, unlockで囲むことにより、そのオブジェクトをロックすることができる。
Object_lock(object);
// 相互排他が必要な処理
Object_unlock(object);
オブジェクトがロックされている部分にはは唯一つのスレッドしか入ることができない。
1つのスレッドがオブジェクトをロックしている間に別のスレッドが lockを行おうとすると、そのスレッドは待ち状態になる。
ロックしているスレッドが unlockを実行することにより、待ち状態になっていたスレッドがロックを獲得することができる。
これにより複数のスレッドが同時にオブジェクトにアクセスすることによる競合を防止(相互排他)することができる。
ロック待ち状態のスレッドが複数個あった場合、ロックを獲得していたスレッドがロックを解放したことによりロックを獲得するするスレッドがどれになるかは不定である。(待ち状態に入った順にロックを獲得するとは限らない)
ロックを解放したスレッドが直ちにロック待ちに入るような場合、いつも特定のスレッドだけがロックを取得し、いつまでたっても獲得できないスレッドが生じる可能性があることに留意しなければならない。
複数範囲の相互排他
ロックは1つの箇所だけではなく、1つのオブジェクトに対して複数個所で相互排他を行うこともできる。たとえば Objectクラスのサブクラスである Dataクラスが、次のように read関数と write関数に相互排他を行った場合、readと writeが複数スレッドで同時に実行することはない。
void Data_read(Data this) {
Data_lock(this);
// read処理
Data_unlock(this);
}
void Data_write(Data this) {
Data_lock(this);
// write処理
Data_unlock(this);
}
複数オブジェクトの間の関係
同じ read, write関数であっても、Dataオブジェクトが異なるものの間では排他は発生しない。たとえば、おなじ Dataクラスの 2個のオブジェクト data1, data2があったとして、data1が read中に別スレッドで data2が writeするようなことはできる。
他の資源の排他
オブジェクトと任意の資源を組み合わせることにより、ロックしたデータ以外の資源の排他制御も行うことができる。たとえば次の例は、char配列 arrayは自身をロックする仕組みを持たないが、lockObjectと組み合わせることにより相互排他を行うことができる。
arrayにアクセスするときは lockObjectのロックを取得し、アクセスが終わったときにロックを解放すれば良い。
struct Array {
Object lockObject;
char array[128];
};
注意事項
注意が必要なのは、unlockを確実の行うのはプログラマの責任である。lockされている期間に何の配慮も無く例外を送出したり longjmpを行った場合 unlockされなくなり、閉塞状態に陥る。
また、Objectクラスの初期化が完了するまでは排他制御は保証されない。これは、コンストラクタの中で自オブジェクトをコールバック登録することなどにより、コンストラクタ完了前にコールバックされるような場合に問題が発生する。
最近、開発室の床に転がって眠ってしまい、気づくと朝になってることがある。
倉庫から梱包用エアクッションを持ってきて枕代わりにしていたが、先週日本製枕を買ってきた。