ミューテックス
ミューテックス (英: mutex) とは、コンピュータプログラミングにおける技術用語。クリティカルセクションでアトミック性を確保するための排他制御や同期機構の一種である。「mutex」という語は「mutual exclusion」 (相互排他、排他制御) の省略形である。ここでは、狭義の排他制御について述べる。
概要
[編集]セマフォをクリティカルセクションの排他制御に用いる時、セマフォでは(初期値が1でなければ)複数のタスクがクリティカルセクションに入ることを許可するのに対し、ミューテックスでは同時に一つのタスクのみがクリティカルセクションに入ることを許可する(ここで言うタスクとは、スレッドまたはプロセスを指す)。挙動はセマフォ変数の初期値を1にする事と等価。このようなタスク優先度とリンクしないミューテックスを、バイナリセマフォと呼ぶ場合もある。
狭義には、ミューテックスの場合にそれをロック(P操作)したタスクのみがアンロック(V操作)できるのに対して、セマフォではその様な制約はない。また、ミューテックスには、優先度逆転を防止するための優先度継承 (Priority Inheritance) 機能や、デッドロックを防止するための優先度上限プロトコル (Priority Ceiling Protocol) 機能などが拡張されていることがある。
一般的には異なるタスク間で排他制御を行いたい時に使用するが、マルチタスク環境ではプロセスの多重起動を防止する用途にも使える。
ミューテックスの起源はディジタル・イクイップメント・コーポレーションにおける、機材の使用管理が由来である。使用可能な機材の上には小さな旗が置かれ、その旗を手にいれた者がその機材の使用権利を獲得する[1]。この排他制御からヒントを得たVMS開発チームはVMSの排他制御にミューテックスを実現した。そしてその技術はデヴィッド・カトラーを筆頭としたVMS開発チームと共にMicrosoft Windows NTにも移された(著:戦うプログラマー[要文献特定詳細情報]より)。
POSIXスレッド (Pthreads) のミューテックスは、VMSやWin32と違い、自らP操作したミューテックスをさらにP操作するとデッドロックを起こす(そして誰もロックを解除できなくなる)。そのため若干取り扱いは不便である。再帰ロックを許可する場合は、ミューテックスの初期化時にPTHREAD_MUTEX_RECURSIVE
を設定した属性を使用する必要がある。
ミューテックスを実現する方法は様々あり、大きくハードウェアによる方式とソフトウェアによる方式に分けられる。ソフトウェアによる方式にも、様々なアルゴリズムが提案されている。詳しくは、排他制御の実施を参照。
ミューテックスの使用
[編集]ミューテックス機構を使用するには、プログラムでミューテックスオブジェクトを作成する必要がある。ミューテックスオブジェクトはシグナルおよび非シグナルの二つの状態を持ち、それぞれミューテックスオブジェクトが所有可能および所有不可能である事を表す。
クリティカルセクションへの進入
[編集]クリティカルセクションに入るにはミューテックスオブジェクトの所有を行う。所有というのはインスタンスの作成という意味ではなく、クリティカルセクションへの進入権を得るという事である。この時、ミューテックスオブジェクトの状態によってクリティカルセクションに入れるかどうかが異なる。
- シグナル状態
- すぐにクリティカルセクションに入る事ができ、処理を続行できる。この時、ミューテックスオブジェクトはシグナル状態から非シグナル状態になり、これによって後続のタスクが同時にクリティカルセクションに入る事を防ぐ。
- 非シグナル状態
- クリティカルセクションに入る事はできず、通常はシグナル状態になるまで(ミューテックスオブジェクトが解放されるまで)タスクは待機状態になる。実装によってはタイムアウトを指定する事ができ、指定した期間内にミューテックスオブジェクトが解放されなければ所有は失敗し、タスクに処理が戻る。この時、タスクは所有が失敗した事を感知し、処理を中断するべきである。
クリティカルセクションからの離脱
[編集]クリティカルセクションから抜ける時はミューテックスオブジェクトを解放する。解放されたミューテックスオブジェクトは非シグナル状態からシグナル状態に戻り、これによって後続のタスクはミューテックスオブジェクトを所有する事が可能になる。
環境ごとの使用法
[編集]POSIX
[編集]プロセス間の場合
[編集]Pthreadsのミューテックスオブジェクトを初期化する際に使用する属性pthread_mutexattr_t
に対して、pthread_mutexattr_setpshared()
関数にてPTHREAD_PROCESS_SHARED
を指定する。ただしサポートされない環境もある。
プロセス内(スレッド間)の場合
[編集]Pthreadsのミューテックスオブジェクトpthread_mutex_t
を使う。
Windows
[編集]プロセス間の場合
[編集]プロセス間の排他制御には、以下の方法がある。
- Mutex - ミューテックスオブジェクトを使う。初期化には
CreateMutex()
Win32 API関数を使う。 - Metered Section - Critical Sectionの拡張であり、
CreateMeteredSection()
関数の実装例がMSDNに記載されている。Mutexより高速であるとされている[2]。[1][リンク切れ][2][リンク切れ]
プロセス内(スレッド間)の場合
[編集]プロセス間でミューテックスを共有する必要がない場合は、以下の方法がある。
- Critical Section - クリティカルセクションオブジェクトを使う。初期化には
InitializeCriticalSection()
Win32 API関数を使う。Mutexより高速である。 - Mutex - 無名のミューテックスオブジェクトを使う。
MFCにはC++のコンストラクタ/デストラクタによるRAII機構を利用した、Win32同期オブジェクトのラッパークラスCMutex
[3]やCCriticalSection
[4]が用意されている(実際にはCSingleLock
と組み合わせて使用することが多い[5])。
C++
[編集]PthreadsやWin32 APIを直接使用するか、あるいは以下に述べるようなポータブルなライブラリを使用する。
プロセス間の場合
[編集]Boost C++ライブラリのboost::interprocess::interprocess_mutex
などを使う。内部的には共有メモリやメモリマップトファイルを使って実装されることもある[6]。Windows実装の場合はWin32ミューテックスを使う実験的コードも存在する[7]。
プロセス内(スレッド間)の場合
[編集]Boost C++ライブラリのboost::mutex
や、C++11以降で標準化されたstd::mutex
を使う。再帰ロックを許可する場合は、boost::recursive_mutex
やstd::recursive_mutex
を使う。通常はlock_guard
やunique_lock
といったロック管理クラスと組み合わせて使用することが多い[8][9]。
.NET
[編集]プロセス間の場合
[編集]System.Threading.Mutex
クラスを使う。
プロセス内(スレッド間)の場合
[編集]System.Threading.Monitor
クラスが用意されているが、このクラスを使用した言語組み込みの同期機能も用意されている。例えばC#では lock
ステートメント、Visual Basic .NETでは SyncLock
ステートメントで、クリティカルセクションを任意のロックオブジェクトにより相互排他ロックすることが可能である。ただし、this
オブジェクトやSystem.Type
インスタンスなど、ロックに使用してはいけないとされているオブジェクトもある[10]。また、メソッド全体を包含する場合は、[MethodImplAttribute(MethodImplOptions.Synchronized)]
属性[11]を適用することができる(Javaのsynchronizedメソッドに相当)。ただしC# 5.0/VB.NET 11で追加されたawait/Await演算子を含むコードブロックをlock/SyncLockでロックすることはできず、System.Threading.SemaphoreSlim
クラスなどを使用する必要がある。
C++/CLI向けにはRAIIを応用したmsclr::lock
クラスが用意されている[12]。
Java
[編集]Javaにはミューテックスクラスが存在せず、パーミッション数1で初期化したjava.util.concurrent.Semaphore
クラスで代用する。また、スレッドの同期機構が言語仕様に組み込んであり、synchronized
ブロックにて任意のオブジェクトをロックオブジェクト(ミューテックス)として使用できる。synchronized
メソッドではthis
オブジェクトもしくはjava.lang.Class
がロックに使われる。プロセス間で使用可能なミューテックスは直接サポートされておらず、ファイルロック (java.nio.channels.FileChannel
, java.nio.channels.FileLock
) を利用する必要がある。
μITRON仕様
[編集]3.0仕様以前には、ミューテックスは存在しない。広義のミューテックスはセマフォで代用することは可能であるが、優先度逆転を防げない。 しかしながら、3.0仕様準拠OSでも、実装独自に優先度逆転を防止できるミューテックスが存在する可能性はある。
4.0仕様以降では、優先度上限および優先度継承をサポートするミューテックスオブジェクトが追加された。しかし、スタンダードプロファイルには含まれておらず、実質オプション扱いである。
脚注
[編集]- ^ VAX & VMS ものがたり - 20年目を迎えた VAX&VMS -
- ^ 短時間でできる汎用の同期オブジェクト | MSDN, Internet Archive
- ^ CMutex Class | Microsoft Learn
- ^ CCriticalSection Class | Microsoft Learn
- ^ Multithreading: How to Use the MFC Synchronization Classes | Microsoft Learn
- ^ Synchronization mechanisms - 1.81.0
- ^ interprocess/include/boost/interprocess/sync/interprocess_mutex.hpp at boost-1.81.0 · boostorg/interprocess | GitHub
- ^ mutex - cpprefjp C++日本語リファレンス
- ^ recursive_mutex - cpprefjp C++日本語リファレンス
- ^ lock ステートメント - C# リファレンス | Microsoft Docs
- ^ マルチスレッド処理のためのデータの同期 | Microsoft Docs
- ^ lock クラス | Microsoft Docs