CThread::notify/notifyAll/wait




Javaの同期の仕組みとして、synchoronizedと並んで便利なのは、waitとnotify/notifyAllです。というか、それしか無いと言っても過言では無いんですけど。(笑)

あるオブジェクトに対して、waitを行なうとします。

枠-1
        some_object.wait();


そうすると、このスレッドは、そのオブジェクトのウェイトセット(待機スレッドリスト)に並びます。つまり、このスレッドの動作はいったん停止します。このあと、他のスレッドが、このオブジェクトに対して、

枠-2
        some_object.notify();


とすれば、さきほどウェイトセットに並んでいたスレッドは、目覚めて、再度活動を開始します。特筆すべき点は、複数のスレッドが、waitによって、そのオブジェクトのウェイトセットに並ぶことが出来るということです。

すなわち、ウェイトセットとは、Win32で言う、Event(yaneSDK2nd/3rdのCEvent)のリストであると考えるとわかりやすいでしょう。notifyとnotifyAllとの違いは、前者は、ウェイトセットで待っているスレッドのうち、ひとつだけ目覚めさせるのに対して、後者は、ウェイトセットに並んでいるスレッドすべてをたたき起こすということです。

このwait〜notify/notifyAllによるマルチスレッドプログラミングについては、Javaのマルチスレッド本、たとえば、結城浩先生の「Java言語で学ぶデザインパターン言語 マルチスレッド編」でも読んでいただくとして、これと同じ機構をC++で実現するために、私はyaneSDK3rdでCLockObjectというクラスを用意しました。

wait〜notify/notifyAllを行ないたいクラスのメンバとして、CLockObjectを持たせ、こいつに対して、wait〜notify/notifyAllを行なえば済むようになっています。

スレッドクラスは特に、wait〜notify/notifyAllを行なう機会があるのは、目に見えているので、CThreadにはあらかじめ、CLockObjectを持たせてあり、CThreadに対して、wait〜notify/notifyAllメソッドを定義してあります。(メンバであるCLockObjectに委譲する)

waitの面白いところは、ウェイトスレッドに並ぶ瞬間に、そのオブジェクトへのlockをいったん解放する部分です。そして、他スレッドからnotifyで、叩き起こされたときに、再度、オブジェクトへのlockを獲得します。このへん、よく出来てるなーと思います。

※ 「いったん解放」というのを見て、疑問に思った人もおられるでしょう。解放するということは、確保していることが前提となっているのか?と..。その通りで、waitは、ロックを持っているスレッドからしか呼び出すことは出来ません。ロックを持っていないスレッドが、wait,notify,notifyAllを呼び出すと、java.lang.IllegalMoniterStateExceptionが投げられます。yaneSDK3rdでも、lockを持っていないスレッドがこれらのメソッドを呼び出したとき、IllegalMoniterStateExceptionという例外を投げるようになっています。