[[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 を有限に抑えることができる。