CCriticalSection




さて、スレッドマネージャについての説明がひととおり終わったところで、スレッドクラスについての説明をしていきたいと思います。

スレッドクラスは、CThreadです。これは、ある程度、Javaスレッドを意識して作ったものです。

Javaのスレッド機構は何だかんだ言って、よく出来ています。言語にスレッドを組み入れているだけのことはあります。

まず、Javaのsynchronized修飾子から説明します。Javaでは、各オブジェクトがCriticalSectionのようなものを持っていると考えるとわかりやすいです。

list-1
    synchronized void method() {
    }


と書いた場合、このメソッドを実行中、他のスレッドはこのメソッドに入ってこれません。

yaneSDK2nd/3rdにある、CCriticalSectionは、Win32のCriticalSectionのwrapperで、次のように使います。

list-2
    CCriticalSection cs;
    cs.Enter();
    {
        //  ここの動作はatomicになる
    }
    cs.Leave();


atomicとは、「原子の」という意味で、これ以上分割できないということです。マルチスレッドの用語で、「複数のスレッドが同時に実行できない」ことを意味します。あるスレッドが、CriticalSectionに入っているとき、他のスレッドは、Enter( )の部分で待たされます。そのあと、CriticalSectionを実行しているスレッドが、Leave( )で、CriticalSectionを抜けると、はじめて、Enter( )で待たされていたスレッドがひとつだけ、CriticalSectionのなかに入ってこれます。

つまり、Enter〜Leaveで囲まれた部分は、一度に、ひとつのスレッドしか実行できません。

さて。CriticalSectionを多重にEnterすることは出来るのでしょうか?

list-3
    CCriticalSection cs;
    cs.Enter();
    {
        cs.Enter();
        {
            //  ここは実行されるのか?
        }
        cs.Leave();
    }
    cs.Leave();


これは、出来ます。出来ても出来なくても同じように思われるかも知れませんが、そうでも無いのです。なぜなら、あるクラスが、

list-4
    class CHoge {
    void f() {
        GetCriticalSection()->Enter();
            g();
        GetCriticalSection()->Leave();
    }
    void g() {
        GetCriticalSection()->Enter();
            //  何か屋さんで何かを買う処理
        GetCriticalSection()->Leave();
    }

    CCriticalSection* GetCriticalSection() { return& cs_; }

private:
    CCriticalSection cs_;
};


というように、他のメソッドを呼び出すことがあって、そのメソッドがすでにCriticalSectionで囲まれているかも知れないからです。

さて、上のプログラムと等価なプログラムをJavaで書けば、

list-5
    public class CHoge {
        synchronized public void f(){
            g();
        }
        synchronized public void g(){
            //  何か屋さんで何かを買う処理
        }
    }


となります。

つまり、Javaのsynchronizedメソッドは、thisが、CriticalSectionを抱えていて、そのメソッドに入るときに、CriticalSectionに入り、そのメソッドを抜けるときに、CriticalSectionから抜ける、とイメージすれば理解しやすいと思います。

Javaには、synchronized文というのもあって、

list-6
    synchronized    (オブジェクト) {
        //  車に跳ねられて、でっかい鯉に食べられる
    }


とやれば、そのオブジェクトのロックを獲得することが出来ます。これと等価なプログラムをC++で書くならば、(そのオブジェクトがさきほどの例のように)CCriticalSectionをメンバとして持っており、GetCriticalSectionで、それを取得できるとして、

list-7
    オブジェクト.GetCriticalSection()->Enter();
    {
        //  車に跳ねられて、でっかい鯉に食べらそうになる
    }
    オブジェクト.GetCriticalSection()->Leave();


と書けば同じ意味になります。

ところで、yaneSDK3rdでは、積極的に例外を用います。(用いることが出来ます)
そこで、上のようなプログラムを書くと、でっかい鯉に食べられそうになっているときに、例外が発生すると、Leaveされないままになってしまいます。

そこで、次のようなクラスを用意します。(これは、yaneSDK3rdに有ります)

list-8
class CCriticalLock {
public:
    CCriticalLock(ICriticalSection* cs) : m_cs(cs) { m_cs->Enter(); }
    ~CCriticalLock() { m_cs->Leave(); }
protected:
    ICriticalSection* m_cs;
};


何のことは無い、コンストラクタで、CriticalSectionに入って、デストラクタで抜けるだけのクラスですが、次のようにコーディングしておけば、例外が発生したときこのデストラクタが呼び出され、CriticalSectionからはLeaveすることが保証されます。

list-9
    void f() {
        CCriticalLock guard(GetCriticalSection());

    }


厳密にはJavaのsynchronizedメソッドは、例外が発生して、そのメソッドから抜けるときにthisへのLockは解除されるので、上のと等価だと考えたほうがよさそうです。