ロック (計算機科学)計算機科学におけるロック(英: lock)とは、計算機システム内に複数の動作主体(プロセスやスレッドなど)のある環境で、データやデバイスなどの共有計算資源(共有リソース)へのアクセス制限を課す同期機構のひとつである。ロックは相互排他の並行性制御ポリシーを強制する手法のひとつでもある。アクセス制限を課す動作を「ロックする」「ロックを取得する」「ロックを獲得する」(acquire lock) などと表現する。また対義語として、制限を解除することをアンロック(英: unlock)と呼び、その動作を「アンロックする」「ロックを解放する」「ロックを解除する」(release lock) などと表現する[注釈 1]。 概要ロックは、複数の動作主体が同一のリソースに対して状態を変更する動作を行うとき、不整合な状態(競合状態)が起こらないように制御する並行性制御において用いられる手法の一つである。ある動作主体がロックしたリソースへは、基本的には他の主体による利用は妨げられる。これを排他制御と呼ぶ。実際には、完全に利用をさせないロックは性能低下が著しいため、複数の主体が取得可能なロックや、他者の読み出しのみ許可するなど、複数のモード(レベル)のロックを用意し必要に応じて使い分ける。 一言でロックといった場合でも、他主体の動作を妨げる基本的機能だけを指す場合、妨げられた主体の待ち動作を含む場合とさまざまである。 動作主体としてプロセスの場合、プロセス間ロック機構がOSによって提供される。スレッドであれば、スレッドライブラリやスレッドを制御するスレッド機構により提供される。動作主体がトランザクションであれば、トランザクションモニターにより提供される。あるいは、リソースがファイルであればファイルロックとなる。 スレッドにおけるロック種類スレッドの場合、関連付けられたデータにアクセスする前にロックを獲得するよう各スレッドが協力するものであって、ロックに強制力はない(これを助言ロック; advisory lock と呼ぶ)。システムによっては強制ロック (mandatory lock) を実装しており、ロックされたリソースに許可されていないアクセスを行おうとしたときに例外が発生するようになっている。 (2値の)セマフォは最も単純なロックである。共有モード(リードオンリー)か排他モード(リードライト)かは区別されない。他の方法では、複数のスレッドがリードオンリーアクセスのためにロックを共有する共有モードを用意している。共有 (shared)、排他 (exclusive)、削除目的 (intend-to-exclude)、更新目的 (intend-to-upgrade) などのモードが広く実装されている。 以上のようなロックの種類とは別に、ロックによってスレッドの進行が妨げられたときの動作によっても分類できる。多くのロックは、ロックされたリソースへのアクセスが可能となるまで、プロセスの実行をブロックする。スピンロックは単純にロックを獲得できるまでスレッドが待つ(スピンする)。これは待つ期間が短い場合は効果的で、オペレーティングシステムによるプロセスのスケジューリングのオーバーヘッドもかからない。ただし、長時間ロックされたままの場合は非常に無駄である。 実装スレッドにおいてロックはメモリ上の値を用いて実装される。複数のスレッドが同時に値を取得・変更できない必要があるが、これを効率的に実装するため、ロックは一般にハードウェアによるサポートを必要とする。通常、1つ以上のアトミック命令を使用する(「テスト・アンド・セット」、「フェッチ・アンド・アッド」、「コンペア・アンド・スワップ」など)。これらの命令は、プロセスがロックが獲得されていない(フリーである)ことをチェックするとともに、不可分な(アトミックな)処理としてロックを獲得できる。 プロセッサが1つであれば、割り込みを禁止して命令列を実行することで同等の効果が得られる。しかし、マルチプロセッサではこれでは不十分である。マルチプロセッサ環境でロックをサポートすることはハードウェアとソフトウェアの複雑なサポートを要し、同期の主要な課題でもある。 アトミックな操作が必要とされる理由は、並列処理では複数のタスクが同じロジックを同時に実行している可能性があるからである。例えば、以下のC言語のコードを考えてみよう。 if (lock == 0) {
/* ロックがフリーなのでセットする */
lock = myPID;
}
複数のタスクが並行して動作可能なら、上記のコードはロックを獲得できるとは言えない。どちらのタスクもロックがフリーであると判断し、どちらもロックに自身のIDをセットして獲得しようとするが、他のタスクも同時にロックを獲得しようとしていることを知らない。アトミックなロック操作ができないときは、デッカーのアルゴリズムやピーターソンのアルゴリズムで代替することもできる。 ロックの不注意な使用によりデッドロックが生じることがある。これはプロセスがあるロックを獲得した状態で別のロックを獲得しようとしたときに発生する。もし2番目のロックが別のプロセスに獲得されていれば、最初のプロセスはブロックされる。2番目のプロセスが最初のプロセスが獲得済みのロックを獲得しようとすると、システムは「デッドロック」状態となり、どちらのプロセスもブロックされたままとなる。これを防ぐための(あるいは回復するための)様々な戦略が設計時や実行時に採用されている。例えば、各ロックが保護するデータの種類によってロックに優先順位を設定し、順位通りでないとロックを獲得できないようにするなどの対処がある(ただし、同じ種類のデータを2つロックしなければならないときなどはデッドロックに注意しなければならない)。 言語によっては組み込みの構文規則によってロックをサポートしていることもある。C#での例を以下に示す。 /// <summary>
/// 口座のモニター。
/// </summary>
class Account {
long val = 0;
readonly object thisLock = new object();
public void Deposit(long x) {
// 一度に1スレッドしか、このブロックを実行できない。
lock (thisLock) {
val += x;
}
}
public void Withdraw(long x) {
// 一度に1スレッドしか、このブロックを実行できない。
lock (thisLock) {
val -= x;
}
}
}
Javaの Javaにおけるメソッド宣言に対する [MethodImpl(MethodImplOptions.Synchronized)]
public void SomeMethod() {
// 何かを実行する。
}
粒度ロックの粒度 (granularity) を説明する前に、ロックに関する3つのコンセプトを説明する。
従って、同期のためのロックの数を決定する際に、ロックのオーバーヘッドとロックの競合がトレードオフの関係にある。 ロックの重要な特性として粒度 (granularity) がある。粒度とは、ロックが保護するデータの大きさである。一般に粗い粒度(ロック数が少なく、各ロックが大きなデータ領域を保護する)ではロックのオーバーヘッドは小さいが、複数プロセスが並行動作するときの性能は低下する。これは粗い粒度ではロックの競合が発生し易いためで、ロックによってプロセスがブロックされる確率が非常に高くなる。反対に細かい粒度(ロック数が多く、各ロックが非常に小さなデータを保護する)ではロック自体のオーバーヘッドは増大するが、ロックの競合は低減する。また、ロック数を増やすとデッドロックの危険性が増す。 データベース管理システムでは、ロックは粒度によって、レコード単位、データページ単位、テーブル全体などを保護対象とする。テーブル全体などの粗い粒度のロックはシングルユーザーで性能向上させるのに有利であり、レコード単位などの細かい粒度のロックは複数ユーザーでの性能向上に有利である。 データベースのロックデータベースでは、ロックはトランザクションの同時性を保証する手段として使うことが出来る。すなわち、トランザクション処理が並行して行われるとき(インタリービング式トランザクション)、ツーフェーズロックを使ってトランザクションの並行実行が直列化されたトランザクションと等価であることを保証する。しかし、データベース内のロックの副作用としてデッドロックが発生することがある。デッドロックはロック順序を事前に定義しておくことで防いだり、待ち状態グラフを使って検出したりする。データベースの一貫性のためにロックを使う以外の手段として、完全に順序が決定されるグローバルなタイムスタンプを使用することでデッドロックを防ぐこともある。 データベース上の複数のユーザーの同時並行的要求に対応するための機構があり、更新をしそこなったり不正な情報を読み取らせることを防ぐことを目的としている。この場合のロックは悲観的ロックと楽観的ロックに分けられる。
ロックにまつわる問題ロックによるリソース保護とスレッド/プロセスの同期には以下のような問題がある:
ロックを避ける並行性制御戦略としてブロックしない同期手法を使うことがあげられる。例えば、lock-freeプログラミング技法やトランザクショナルメモリなどがある。しかし、そういった技法を使ったとしてもロックにまつわる問題を全て完全に回避できるわけではない。 どんな並行性制御戦略でも、OSのより基礎的なレベルでの実際のロック機構の実装を必要とし、単にアプリケーションのレベルで実装の詳細をロックを必要としないように見せているだけである。「問題」は依然として存在するが、それを扱うのはアプリケーションではない。実際、ロック機構は最終的にはCPUハードウェアの提供する不可分操作技法に依存している。 言語サポート→「バリア (計算機科学)」も参照
プラットフォーム(OS)やプログラミング言語によっては、様々なロックをサポートしている。
脚注注釈出典
関連項目外部リンク
|
Portal di Ensiklopedia Dunia