ACT-3前回の二重起動防止は、2回目以降に起動されたプロセスから、最初のプロセスに通知を送ることができましたね。応用すれば、ファイル名を渡したりするのも自由自在です。ところが、実はこのプログラムは完全なものではないのです。前回作成されたプログラムで不完全なことというのは、プロセス間で同期が取られていないということです。これがいったいどういうことなのか、そしてどうすれば解決できるのかを説明していくことにしましょう。前回作成されたプログラムでは、かなり低い確率ではありますが、1つ問題が出てきます。最初にファイルマッピングオブジェクトを作成したプロセスが最初に起動されたプロセスであるとみなされるわけですが、このプロセスがファイルマッピングオブジェクトを作成してから、その共有メモリにウィンドウハンドルを格納するまでの間に、CPUのタイムスライスが2回目に起動されたプロセスに移って、それが共有メモリからウィンドウハンドルを読み込もうとするかもしれないということです。2回目に起動されたプロセスは、まだウィンドウハンドルの格納されていない共有メモリから読み込もうとするかもしれないということです。これではメッセージを送ることはできなくなってしまいます。このようなことは、2つのプロセスがほとんど同時に起動されなければ起こらないことですし、同時に起動したとしても、起こる可能性は非常に低いことですが、絶対に起こらないとも言いきれないことです。ウィンドウハンドルの場合は終了時まで値は変わらないため問題にならないのですが、ウィンドウハンドル以外のデータをやり取りするために共有メモリを使用するとなると、さらに問題が出てきます。2つ以上のプロセスが同時に共有メモリにアクセスしてしまう可能性があるということです。これでは、せっかく送ろうとしたデータが壊れてしまう可能性がありますね。したがって、こういった事を起こさないようにプログラムを組む必要があります。この問題を防ぐには、プロセス間で同期(synchronization)を取ることが必要になります。同期を取るとはすなわち、ある条件が整うまでそのプロセスを(正確にはスレッドを)眠らせてしまうということです。あるスレッドが共有メモリにアクセスしている間、その作業が終わるまでは、共有メモリにアクセスしようとした他のスレッドはその実行を停止させておきます。作業が終わった時点で、停止されていたスレッドが動き出すようにするのです。以前に、起動したサブプロセスの終了を待つ方法を紹介しましたが、これも同期の1つになります。ミューテックスオブジェクトによる同期プロセス間で同期を行なうにはミューテックスオブジェクトを使います。ミューテックスオブジェクトには『被所有者』としての機能があり、スレッドによって「所有されている」状態と「所有されていない」状態のいずれかの状態にあるということは以前に述べました。そのときはこの『所有』の概念は使われませんでしたが、今回はこれを使うことになります。ACT-1』を参照してください。簡単に復習しておきましょう。どれか1つのスレッドに所有されている状態ということでしたね。シグナル状態になるまでスレッドのを停止ということでした。オブジェクトがノンシグナル状態になるというものがあるのです。ミューテックスオブジェクトに対して待機関数WaitForSingleObjectを呼び出すという動作は、ミューテックスオブジェクトの所有権を要求するということなのです。したがって、ミューテックスオブジェクトと待機関数を組み合わせたときの動作は、次のようになることが分かります。ミューテックスオブジェクトに対して待機関数を呼び出したとき、どのスレッドもそのオブジェクトを所有していない(オブジェクトがシグナル状態である)場合は、呼び出し側スレッドはオブジェクトの所有権を得てそのまま処理が続行されます。このとき、オブジェクトは所有された状態に移るわけですから、オブジェクトはノンシグナル状態になります。待機関数が呼び出されたとき、他のスレッドがそのオブジェクトを所有している(オブジェクトがノンシグナル状態である)場合は、所有スレッドがオブジェクトの所有権を解放するまで(所有スレッドがオブジェクトを所有しなくなり、オブジェクトがシグナル状態になるまで)、呼び出し側スレッドの実行を停止させます。オブジェクトがシグナル状態になったとき、呼び出し側スレッドは所有権を得ることができて、オブジェクトは再びノンシグナル状態になります。以上のことから、ミューテックスオブジェクトを使ってプロセス間の同期を実現できることがわかります。ミューテックスオブジェクトが今回のプログラム中でどのような意味を持つのかを説明しておきましょう。ミューテックスオブジェクトの所有権を持つことができるスレッドはただ1つだけで、同時に2つ以上のスレッドがオブジェクトを所有することはない、ということは分かりますね。そこで、「共有メモリにアクセスするスレッドはミューテックスの所有権を持っていなければならない」という条件をつけたとしたらどうでしょうか。このようにすれば、共有メモリにアクセスできるスレッドを同時にただ1つまでに限定することができますよね。共有メモリにアクセスする権利という考え方が、今回の同期を行なうための重要な考え方になります。ミューテックスオブジェクトの所有権の解放ミューテックスオブジェクトを所有しているスレッドが、そのオブジェクトを手放すための方法をこれまで説明していなかったので、ここで説明しておきましょう。ミューテックスオブジェクトの所有権を解放するにはReleaseMutex関数を呼び出します。ミューテックスのハンドル);この関数は、呼び出しスレッドが指定されたミューテックスオブジェクトを所有している場合に、そのオブジェクトの所有権を解放します。この関数によってミューテックスオブジェクトが解放されると、そのオブジェクトはシグナル状態になります。待機関数によって同じオブジェクトがシグナル状態になるのを待って停止されている別のスレッドがあれば、そのスレッドに所有権が渡されて、そのスレッドの実行が再開されます。実際の手順では、今回の手順を説明しましょう。ミューテックスオブジェクトを作成する。このときには所有権を取得しない。待機関数を実行し、ミューテックスオブジェクトの所有権を得る。このとき、別のスレッドがオブジェクトを所有していれば、それが解放されるまで実行が停止される。共有メモリとしてのファイルマッピングオブジェクトを作成する。ファイルマッピングオブジェクトがすでに作成されていたかどうかを調べる。作成されていなかった場合(初回起動とみなす)ウィンドウをサブクラス化して、メッセージを取得するように設定する。共有メモリにメインウィンドウのハンドルを格納する。ミューテックスオブジェクトの所有権を解放する。メッセージが送られてくるのを待つ。終了時にファイルマッピングオブジェクトとミューテックスオブジェクトをクローズ。作成されていた場合(2回目以降の起動とみなす)共有メモリからウィンドウハンドルを読み込む。ミューテックスの所有権を解放する。ファイルマッピングオブジェクトとミューテックスオブジェクトのハンドルをクローズして終了ミューテックスオブジェクト作成時には、オブジェクトの所有権を強制的に取得するかどうかを指定することができますが、今回はオブジェクト作成時に所有権を取得していません。ここで所有権を取得してしまうと、もしその時点で他のスレッドがこのミューテックスオブジェクトを所有していて共有メモリにアクセスしていたとしても、それを無視して強制的に所有権を奪ってしまうことになるからです。これでは、ミューテックスオブジェクトを使う意味がありません。ミューテックスオブジェクトの所有権を得るには待機関数を使わなければなりません。指定された名前のミューテックスが存在する場合には、強制的に所有権を奪うということはないようです。所有権を得るためには待機関数を呼び出すという形に統一させるようにしておきたいと思います。上の手順を見て気づいたことがあると思います。それは、起動されたプロセスが最初に起動されたものかどうかを取得するのに、ミューテックスオブジェクトが存在するかどうかではなく、ファイルマッピングオブジェクトが存在するかどうかで判断しています。ミューテックスオブジェクトの存在で判断しないのかというと、最初にミューテックスオブジェクトを作成したプロセスが必ずしも最初にミューテックスオブジェクトの所有権を得るとは限らないからです。最初にミューテックスオブジェクトを作成したスレッドが待機関数を呼び出してミューテックスオブジェクトの所有権を得ようとするまでの間に、2番目にミューテックスオブジェクト作成関数を呼び出したスレッドがさらにそのまま待機関数を呼び出して先に所有権を得てしまうという可能性もあるためです。(起こることなどないとは思うのですが、可能性として......。このようなことが起こってしまわないように、このような方法を使うのです。サンプルスクリプトサンプルスクリプトを作成してみます。実行させたときの動作は前回のものと同じになります。前回と同じく、処理を各部分に分けてみていくことにします。起動時(共通)まずは以下の処理をするためのスクリプトです。ミューテックスオブジェクトを作成する。このときには所有権を取得しない。待機関数を実行し、ミューテックスオブジェクトの所有権を得る。このとき、別のスレッドがオブジェクトを所有していれば、それが解放されるまで実行が停止される。共有メモリとしてのファイルマッピングオブジェクトを作成する。ファイルマッピングオブジェクトがすでに作成されていたかどうかを調べる。}記述量は前回と比べるとかなり多くなってますが、それぞれの関数の働きを理解してさえいれば、さほど難しいことでもないと思います。名前付きカーネルオブジェクトは、同じ名前空間でオブジェクト名を管理しているため、種類の違うオブジェクトが同じ名前をもつことができません。そのため、ファイルマッピングオブジェクトの名前とミューテックスオブジェクトの名前は別のものでなければなりませんので、注意しましょう。ファイルマッピングオブジェクト未作成時(初回起動とする)ファイルマッピングオブジェクトが作成されていなかった場合(初回起動の場合)の処理です。ウィンドウをサブクラス化して、メッセージを取得するように設定する。共有メモリにメインウィンドウのハンドルを格納する。ミューテックスオブジェクトの所有権を解放する。メッセージが送られてくるのを待つ。終了時にファイルマッピングオブジェクトとミューテックスオブジェクトをクローズ。end今回のスクリプトでは、ミューテックスオブジェクトの所有権は共有メモリにアクセスする権利を表しています。どれか1つのスレッドがミューテックスオブジェクトを独占したままだと、他のスレッドは作業できずに停止したままになってしまいます。WaitForSingleObject関数でミューテックスオブジェクトの所有権を取得してからReleaseMutex関数でそれを解放するまでの作業は、時間をかけずに一気に行なってしまいましょう。途中でwaitやawaitやstopなどを実行してしまってはいけません。ファイルマッピングオブジェクト作成時(2回目以降起動)ファイルマッピングオブジェクトが作成されていた場合(2回目以降の起動の場合)の処理です。共有メモリからウィンドウハンドルを読み込む。ミューテックスオブジェクトの所有権を解放する。