[[Hirofumi Fujii Start Page]]

* Semaphore と mutex [#r754d0da]
mutex は資源の排他的利用を目的として使われる。資源利用を行いたい thread は、まず mutex を lock し、利用が終わったら mutex を unlock する。この間、他の thread は(mutex を lock することができないので)待ち状態に置かれる。

これに対し、semaphore は資源が利用可能になったことを他の thread に伝えるために使われる。通常 thread 間の同期に使われる。消費者-供給者問題の解法として利用されることが多い。

** Mutex の使用例 [#yad767eb]
例として、多数の request が queue に入れてあって、複数の thread が次々に queue から request を取り出して service (do_service())を行うことを考える。

 class request;
 std::queue<request> q;
 
 service_thread()
 {
   while(!q.empty())
   {
     request req = q.front();
     q.pop();
     req.do_service();
   }
 }

さて、これを multi-thread で実行すると、q.empty() が false であることを確認した後、次の step に進む前に、別 thread が queue から request を取り出して、その結果 empty になっている可能性がある。つまり、最初の !q.empty() が意味を持たない。そこで queue の操作中は排他制御を行いたい。

 class request;
 std::queue<request> q;
 mutex m;
 
 service_thread()
 {
   while(1)
   {
     lock(m);
     if(q.empty())
       break;
     request req = q.front();
     q.pop();
     unlock(m);
     req.do_service();
   }
 }

明らかに mutex m は queue と結びついているので、std::queue<request> を継承し、mutex m を member として持つようにすれば、もう少し明瞭になるかも知れない。また lock class を作って unlock を destructor で行うようにするのも一考に値する。

** Semaphore の利用例 [#l31cc029]
上記例題で、request を受け付けて queue に入れる thread も同時に進行させる場合を考える。この場合、queue.empty() だからといって上述の program のように loop を抜けるわけにはいかない。単に request に入れるのが遅れているだけかも知れないからである。queue の empty と thread の終了は別項目として扱う必要がある。

さて解法であるが、empty の時には適当な sleep() を入れてまた loop に入るという手段も考えられるが、ここでは単純に (couting) semaphore を使ってみる。

 class request;
 std::queue<request> q;
 mutex m;
 semaphore sem;
 
 post_thread()
 {
   while(1)
   {
     request req = accept_request();
     lock(m)
     q.push_back(req);
     unlock(m);
     post(sem);
   }
 }
 
 service_thread()
 {
   while(1)
   {
     wait(sem);
     lock(m);
     request req = q.front();
     q.pop();
     unlock(m);
     req.do_service();
   }
 }

ここで、service_thread() の中で q.empty() の条件が無くなっていることに注意する。Semaphore で同期をとっているので wait(sem) から抜けたときには必ず queue に request が存在しているからである。また mutex は queue の操作中に別の thread が書き換えるのを防止している。
ここで、service_thread() の中で q.empty() の条件が無くなっていることに注意する。Semaphore で同期をとっているので wait(sem) から抜けたときには必ず queue に request が存在しているからである。また mutex は queue の操作中に別の thread が、その queue を書き換えるのを防止している。

** 有限の queue の取り扱い [#b0c4b12c]
上記 semaphore の例は service_thread が post_thread より遅い場合は queue はどんどん長くなる。資源は有限であるので、資源を食い尽くした段階で異常終了となる。これはもともと service の処理速度が遅いことが原因なので program の見直しよりも system の見直しで対応すべき場合もある(request の発生を制御できない場合など)。
一方、request の発生を制御できる場合など、queue の size を有限にしておいて、queue が full になったら、request の受け付けを一時中断することで対処できる場合もある。ここでは後者の解法を考える。

上記 semaphore の例題では、semaphore は post してあるが service が受け取っていない reuqest の数、すなわち未処理の request の数を示していた。これに対し、空き request の数を示す semaphore を導入することで、queue の size を有限に抑えることができる。



トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS