ミューテックス

ミューテックス(Mutex)とは



ミューテックス(Mutex)は、コンピュータプログラミングにおいて、複数のスレッドやプロセスが共有リソースに同時にアクセスすることを防ぐための排他制御機構です。これは、クリティカルセクションと呼ばれる、同時に実行されると問題が生じる可能性のあるコード領域を保護するために使用されます。ミューテックスは「mutual exclusion」(相互排他)の略であり、その名の通り、ある時点で一つのタスクのみがクリティカルセクションに入ることができるように制御します。

概要



セマフォも排他制御に利用できますが、ミューテックスは、同時に一つのタスクのみがクリティカルセクションに入ることを保証します。これは、セマフォ変数の初期値を1に設定した場合の動作と等価です。ミューテックスは、タスクの優先度とは独立して動作し、このようなミューテックスはバイナリセマフォと呼ばれることもあります。

また、ミューテックスでは、ロック(P操作)を行ったタスクのみがアンロック(V操作)できるという制約がありますが、セマフォにはこの制約はありません。さらに、ミューテックスには、優先度逆転を防ぐための優先度継承機能や、デッドロックを回避するための優先度上限プロトコルなどの拡張機能が実装されている場合があります。

ミューテックスは、通常、異なるタスク間で排他制御を行いたい場合に利用されますが、マルチタスク環境では、プロセスの多重起動を防止する目的にも使用できます。

ミューテックスの起源



ミューテックスの概念は、デジタル・イクイップメント・コーポレーション(DEC)における機材の利用管理から生まれました。使用可能な機材には旗が置かれ、その旗を手にした者が機材の使用権を得るという仕組みがヒントになりました。この排他制御のアイデアを基に、VMS開発チームがVMSにおける排他制御にミューテックスを実装しました。その後、デヴィッド・カトラー率いるVMS開発チームによって、この技術はMicrosoft Windows NTにも導入されました。

POSIXスレッド(Pthreads)における注意点



POSIXスレッド(Pthreads)のミューテックスは、VMSやWin32とは異なり、すでにロックされているミューテックスを再度ロックしようとすると、デッドロックが発生します。そのため、Pthreadsでのミューテックスの扱いは若干複雑です。再帰的なロックを許可する場合は、ミューテックスの初期化時に `PTHREAD_MUTEX_RECURSIVE` 属性を設定する必要があります。

実装方法



ミューテックスの実装には、ハードウェアによる方式とソフトウェアによる方式があります。ソフトウェアによる方式にも様々なアルゴリズムが提案されており、詳細は排他制御の実施に関する文献を参照してください。

ミューテックスの使用方法



ミューテックスを使用するには、プログラム内でミューテックスオブジェクトを作成する必要があります。ミューテックスオブジェクトは、「シグナル状態」と「非シグナル状態」の2つの状態を持ち、それぞれミューテックスオブジェクトが利用可能か、利用不可能かを表します。

クリティカルセクションへの進入



クリティカルセクションに入るためには、ミューテックスオブジェクトの所有権を取得する必要があります。これは、ミューテックスオブジェクトのインスタンスを生成するのではなく、クリティカルセクションへの進入権を得ることを意味します。この際、ミューテックスオブジェクトの状態によって、クリティカルセクションに入れるかどうかが決まります。

シグナル状態


ミューテックスオブジェクトがシグナル状態の場合、タスクはすぐにクリティカルセクションに入ることができ、処理を続行できます。このとき、ミューテックスオブジェクトはシグナル状態から非シグナル状態に遷移し、他のタスクが同時にクリティカルセクションに入ることを防ぎます。

非シグナル状態


ミューテックスオブジェクトが非シグナル状態の場合、タスクはクリティカルセクションに入ることができず、通常はシグナル状態になるまで(ミューテックスオブジェクトが解放されるまで)待機状態になります。実装によっては、タイムアウトを設定することもでき、指定した時間内にミューテックスオブジェクトが解放されない場合、所有権の取得に失敗してタスクに制御が戻ります。この場合、タスクは所有権の取得に失敗したことを検知し、適切に処理を中断する必要があります。

クリティカルセクションからの離脱



クリティカルセクションから離脱する際には、ミューテックスオブジェクトを解放します。解放されたミューテックスオブジェクトは、非シグナル状態からシグナル状態に戻り、後続のタスクがミューテックスオブジェクトを所有できるようになります。

環境ごとの使用法



POSIX



プロセス間の場合


Pthreadsのミューテックスオブジェクトをプロセス間で共有するには、初期化時に`pthread_mutexattr_setpshared()`関数で`PTHREAD_PROCESS_SHARED`属性を指定します。ただし、環境によってはサポートされない場合があります。

プロセス内(スレッド間)の場合


Pthreadsのミューテックスオブジェクトである`pthread_mutex_t`を使用します。

Windows



プロセス間の場合


プロセス間での排他制御には、以下の方法があります。

Mutex: `CreateMutex()` API関数を用いてミューテックスオブジェクトを作成します。
Metered Section: `CreateMeteredSection()`関数の実装例がMSDNに掲載されている、Critical Sectionの拡張機能です。Mutexよりも高速に動作するとされています。

プロセス内(スレッド間)の場合


プロセス間でミューテックスを共有する必要がない場合は、以下の方法が利用できます。

Critical Section: `InitializeCriticalSection()` API関数を用いてクリティカルセクションオブジェクトを作成します。Mutexよりも高速に動作します。
Mutex: 無名のミューテックスオブジェクトを使用します。

MFC(Microsoft Foundation Class Library)では、C++のRAII(Resource Acquisition Is Initialization)機構を利用した、Win32同期オブジェクトのラッパークラスである`CMutex`や`CCriticalSection`が提供されています。これらは通常、`CSingleLock`クラスと組み合わせて使用されます。

C++



PthreadsやWin32 APIを直接使用するか、ポータブルなライブラリを使用します。

プロセス間の場合


Boost [[C++ライブラリ]]の`boost::interprocess::interprocess_mutex`などが利用できます。これは内部的に共有メモリやメモリマップトファイルを用いて実装されることがあります。Windows環境では、Win32ミューテックスを使用する実験的なコードも存在します。

プロセス内(スレッド間)の場合


Boost [[C++ライブラリ]]の`boost::mutex`や、C++11以降で標準化された`std::mutex`が利用できます。再帰的なロックが必要な場合は、`boost::recursive_mutex`や`std::recursive_mutex`を使用します。これらのミューテックスは、通常、`lock_guard`や`unique_lock`といったロック管理クラスと組み合わせて使用されます。

.NET



プロセス間の場合


`System.Threading.Mutex`クラスを使用します。

プロセス内(スレッド間)の場合


`System.Threading.Monitor`クラスが利用可能ですが、言語組み込みの同期機能も提供されています。C#では `lock` ステートメント、Visual Basic .NETでは `SyncLock` ステートメントを用いて、任意のロックオブジェクトでクリティカルセクションを相互排他ロックできます。ただし、`this`オブジェクトや`System.Type`インスタンスなど、ロックに使用すべきでないオブジェクトも存在します。また、メソッド全体をロックする場合は、`[MethodImplAttribute(MethodImplOptions.Synchronized)]`属性を適用できます(Javaの`synchronized`メソッドに相当)。C# 5.0/VB.NET 11で追加された`await/Await`演算子を含むコードブロックを`lock/SyncLock`でロックすることはできず、`System.Threading.SemaphoreSlim`クラスなどを使用する必要があります。

C++/CLI向けには、RAIIを応用した`msclr::lock`クラスが用意されています。

Java



Javaには、ミューテックスクラスは存在しません。`java.util.concurrent.Semaphore`クラスをパーミッション数1で初期化することで、ミューテックスとして代用できます。また、スレッド同期機構が言語仕様に組み込まれており、`synchronized`ブロックを使って任意のオブジェクトをロックオブジェクトとして使用できます。`synchronized`メソッドでは、`this`オブジェクトまたは`java.lang.Class`がロックに使用されます。プロセス間で利用可能なミューテックスは、直接サポートされておらず、ファイルロック (java.nio.channels.FileChannel, java.nio.channels.FileLock) を利用する必要があります。

μITRON仕様



3.0仕様以前のμITRONには、ミューテックスは存在しません。広義のミューテックスはセマフォで代用可能ですが、優先度逆転問題を防ぐことはできません。3.0仕様準拠のOSでも、実装独自に優先度逆転を防止できるミューテックスが存在する可能性があります。4.0仕様以降では、優先度上限や優先度継承をサポートするミューテックスオブジェクトが追加されましたが、スタンダードプロファイルには含まれておらず、実質オプション扱いとなっています。

関連項目



排他制御
並行計算
クリティカルセクション
セマフォ
デッドロック
スレッド (コンピュータ)
プロセス
マルチタスク

もう一度検索

【記事の利用について】

タイトルと記事文章は、記事のあるページにリンクを張っていただければ、無料で利用できます。
※画像は、利用できませんのでご注意ください。

【リンクついて】

リンクフリーです。