pthread地獄 part 2 (232レス)
上下前次1-新
抽出解除 レス栞
リロード規制です。10分ほどで解除するので、他のブラウザへ避難してください。
93(19): 名無しさん@お腹いっぱい。 [sage] 2008/07/22(火) 12:01:15 AAS
複数のスレッドの終了を待つってどう書くんですか?
マルチプロセスだと、waitpid(2)とかで複数の子プロセスの終了を
待てるんですが、pthread_join()だと、特定のスレッドの終了しか待てません。
例えば、
100個の子プロセスを作成して、親プロセスはwaitpid()で任意の子プロセスの終了を
監視していて、特定の子プロセスが死んだ場合に、そのプロセスの再起動(fork())を
行うという処理を、pthreadで書こうとした場合、どうすれば良いんでしょうか?
そもそも、上記の様な考え方は、プロセスの親子関係が前提となっているので、
この考え方を、親子という関係のないpthreadに持って来る事がおかしいのでしょうか?
95: 93 [sage] 2008/07/22(火) 13:21:54 AAS
それだと、作成したスレッドが死ぬようなイベントが発生したタイミングを
捕まえるという動作ではないですよね。(ポーリングっぽい)
例えば、100個スレッドを作って、その各スレッドがTCPソケットを使って
通信していて、TCPコネクションがcloseされたので、pthread_exit()を
コールしたとか、、ソケットから受け取ったデータを処理している最中に
SIGSEGVで死んだとかした場合に、これら100個のスレッドを常に監視
していて、死んだスレッドを再度作成したいって感じの処理をすっきりと
書きたい場合ってどうやるんでしょう?
スレッドじゃなくてプロセスだったら、子プロセスがexit(2)した場合も、
子プロセスが、SIGSEGVで死んだ場合も、親プロセスがwaitpid(2)してれば
子プロセスが死んだタイミングで親プロセスはそれを知ることが出来るじゃ
ないですか。
これと同じような事をpthreadでやりたいんですが、なんかよく判らんのです。
98(2): 93 [sage] 2008/07/22(火) 14:58:19 AAS
>>96
>>97
確かに、SIGSEGVなどで死ぬ場合は、SIGSEGVを発生させたロジックを実行中のスレッド
のみが死ぬわけではなく、プロセス自体がいなくなりますが、これをハンドリングして
特定のスレッドのみを再起動して処理を継続するってのは変でしょうか?
プログラムのバグも含めて考えると、やっぱりスレッドがSIGSEGVするケースも考慮して
おきたんです。
Webサーバの様なプログラムをマルチスレッドで書くとすると、クライアントから送られて来た
データがメタメタでサーバ側の処理がSIGSEGVしてしまったとか。(だったらちゃんとデータを
処理する前にチェックしろってのは、ちょっと置いといて。)
こういったケースで正常なクライアントとのコネクションも全部潰れてしまうのは、なんだかなぁ
って思ったんです。
99: 93 [sage] 2008/07/22(火) 15:06:19 AAS
あと、条件変数でスレッド間で待ち合わせを行うってのはなんとなく判るんですが、
それと、スレッドの終了を待つってのがどうもうまく結び付きません。
例えば、
ワーカースレッドがもうダメポってpthread_cond_signal()をコール。
メインスレッドは、pthread_cond_wait()で待ってる。
ワーカースレッドはどのタイミングでpthread_exit()をコールすればいいの?
メインスレッドは、どのタイミングでpthread_join()をコールすればいいの?
ワーカースレッドが居なくなったタイミングって条件件数を使えばメインスレッドで
捕まえることって出来ますか?
なんか、この辺りがよく判らんのです。
103: 93 [sage] 2008/07/22(火) 16:20:14 AAS
>>100
>>101
ワーカースレッドとは別にsignalをハンドルするスレッドを、1つもしくは、
ワーカースレッド数分作成しておけば、どのワーカースレッドがSIGSEGV
を発生させたかは判るような気がしてます。
とりあえず、signalはちょっと置いといて、複数のワーカースレッドが
居なくなったタイミング(pthread_exit()をコールしたタイミング)で
メインスレッドがpthread_join()をコールする仕組み(=waitpid())を
作ってみたんですが、単純にpthread_cond_signal()をワーカースレッドで
呼ぶだけだと、メインスレッドがpthread_cond_wait()を実行中じゃない
ケースでとりこぼしちゃうんですね。
メインスレッドがちゃんとpthread_cond_wait()を実行しているタイミングを
ワーカースレッドが認識しないとダメなのか。
なんか、基本的な考え方が間違っている様な気がしてきました。
そもそも、pthread_join()が複数のスレッドを待てない時点で、この様な
事をやりたいという考えがそもそも変な気がしてるんですが、でも、
Windowsなんかのスレッドだと出来るらしいし。
106(1): 93 [sage] 2008/07/22(火) 18:20:41 AAS
>>105
どういうレベルかと言われると良く判らないのですが、
SIGSEGVとか、SIGILLとか、SIGFPEとかのシグナルは、同期シグナルと呼ばれていて、
スレッド側で、signal(3)でハンドラを設定しておいてあげれば、そのシグナルを発生させた
スレッドがシグナルを受け取ってくれるみたいです。
"pthread 同期シグナル" でぐぐった時の2ページ目の最後のマルチスレッドのプログラミング
というSunのPDFへのリンク先の資料に書いてありました。
Solaris10(x86)と、FreeBSD(i386)でサンプルを作ってみたところ、スレッド側で定義した
シグナルハンドラでpthread_self()すると、ちゃんとしたスレッドIDが取得できました。
107: 93 [sage] 2008/07/22(火) 18:55:40 AAS
ちゃんとしたスレッドIDってのは、SIGSEGVを発生させたスレッドIDって言う意味です。
113(1): 93 [sage] 2008/07/24(木) 09:46:56 AAS
複数のワーカースレッドの終了を待つロジックを書いてみた。
/* 全てのワーカースレッドの終了を待つ */
pthread_mutex_lock(&m_end);
while (0 != thread_num) {
while(NULL == thr_end) {
pthread_cond_wait(&c_end, &m_end);
}
nrc = pthread_join(thr_end, NULL);
if (0 == nrc) {
fprintf(stdout, "thread %5d is exited...\n", thr_end);
--thread_num;
thr_end = NULL;
}else{
fprintf(stdout, "Error pthread_join() return %d\n", nrc);
}
pthread_cond_broadcast(&c_end);
}
pthread_mutex_unlock(&m_end);
fprintf(stdout, "ALL thread is exited... thread_num=%d\n", thread_num);
114(1): 93 [sage] 2008/07/24(木) 09:47:40 AAS
こっちがワーカースレッド側
/* メインスレッドに処理終了を通知 */
pthread_mutex_lock(&m_end);
while (NULL != thr_end) {
pthread_cond_wait(&c_end, &m_end);
}
thr_end = pthread_self();
pthread_cond_broadcast(&c_end);
pthread_mutex_unlock(&m_end);
pthread_exit((void *)NULL);
115: 93 [sage] 2008/07/24(木) 09:51:36 AAS
やっと、条件変数の使い方が判った。
添削してもらおうとは思ってないけど、とりあえずいろいろ教えてもらったので
張っときます。
複数の子プロセスの任意のタイミングでの終了を親プロセスが待つって
いうケース(親がwaitpid(2)で任意の子プロセスの終了を待つ)を想定してます。
117: 93 [sage] 2008/07/24(木) 11:24:46 AAS
セマフォでやるってのがいまいちピンと来ないんだけど。
親(メイン)スレッドがいつ居なくなるか判らない複数の子(ワーカー)スレッドの終了を待っていて
どれかの子(ワーカー)が居なくなったら、それをハンドリング(どの子が居なくなったかを認識)するってどうやるの?
122: 93 [sage] 2008/07/24(木) 12:52:22 AAS
>>118
最初はそう考えたんですが、親がcond_waitしてないときに子が親にcond_signal
するケースを考えると、なんか余計に複雑になるような気がして、
>>113
>>114
に落ち着いたんです。
条件変数分けると、mutexも分けないといけないし。
(ん? 条件変数だけ分けてmutexは使い回せばよい?)
もっかい考えてみる。
>>120
ケースバイケースだと思うんだけど、pthreadでプログラム作るときって、detachするのが
どっちかと言うとデフォなの?
>>121
そうなんですよね。
こんなのって定石だと思うんですが、なんでpthread_XXが無いんだろう?
123: 93 [sage] 2008/07/24(木) 13:11:48 AAS
>>121
>ハンドリングまでするならqueueだな。
あー、確かに、queue作って、子が死ぬ前に突っ込んで親がそれを拾えば
うまくいきますね。
子が居なくなるのと親がそれを検出するのの同期を取らなくても良い場合は、
それが一番良さそうな気がしますね。
125(1): 93 [sage] 2008/07/24(木) 13:50:31 AAS
>>118
こんな感じですか。
ワーカー側でcond_broadcast使わなくても良くなったので、無駄なスレッドが
起こされなくなってちょっと軽くなったのかな。
ボス側
pthread_mutex_lock(&m_end);
while (0 != thread_num) {
while(NULL == thr_end) {
pthread_cond_wait(&c_end_boss, &m_end);
}
nrc = pthread_join(thr_end, NULL);
if (0 == nrc) {
fprintf(stdout, "thread %5d is exited...\n", thr_end);
--thread_num;
thr_end = NULL;
}else{
fprintf(stdout, "Error pthread_join() return %d\n", nrc);
}
pthread_cond_broadcast(&c_end_work);
}
pthread_mutex_unlock(&m_end);
ワーカー側
pthread_mutex_lock(&m_end);
while (NULL != thr_end) {
pthread_cond_wait(&c_end_work, &m_end);
}
thr_end = pthread_self();
pthread_cond_signal(&c_end_boss);
pthread_mutex_unlock(&m_end);
pthread_exit((void *)NULL);
126: 93 [sage] 2008/07/24(木) 19:31:08 AAS
pthreadとシグナルについてですが、
同期シグナルは発生要因となったスレッドに送られ、そのスレッド上でシグナルハンドラが起動される。
非同期シグナルは、それを受け取る準備をしているスレッドに送られる。(結果的に、同期的にシグナルを扱うことが出来る)
いずれの場合も、シグナルを受け取ったスレッドでpthread_XXを使ってもうまく動くと思うんですが、間違ってますか?
ようは、SIGSEGVのハンドラからpthread_XXを呼んでみるとうまく動いているように見えるんだけど、
これって、実装(環境)依存なだけなのか、そうでないのかが知りたいんです。
128: 93 [sage] 2008/07/25(金) 10:14:40 AAS
確かにそういうケースだとpthread関数がまともに動く可能性はないかもしれないですね。
私がSIGSEGVを発生させたパターンは単に、NULLアドレスに書き込んでるだけなので、
その辺のデータ(pthread関数が使用している内部データ)を壊してるって訳ではないです。
そもそも、シグナルハンドラからPthread関数が呼べない理由ってのは何故なんでしょう?
Pthread関数の内部データはそのスレッドのスタック上に存在していて、
シグナルハンドラはスレッドとは別のスタックを使って実行されるからって事ですか?
132: 93 [sage] 2008/07/25(金) 12:44:25 AAS
今、気になっているのは、Webサーバの様なサーバプログラムで、ボスは常にaccept()待ち。
クライアントからの接続があったら、ワーカーを起動して、そのあとの処理はワーカに任せる。
といった、定番的なネットワークサーバを書く場合に、いわゆるfork()モデルと、スレッドモデルで
どのような差があるのか(特にエラー発生時において)という事です。
なので、ワーカー側の処理ってのは、基本的に独立していてワーカー同士で共有を行うデータも
不要であると考えています。
非同期シグナルも使う必要は無いと考えています。(多分)
fork()モデルの場合は、ワーカプロセスが同期シグナル(SIGSEGV,SIGILL等)を発生させたとしても、
他のワーカープロセスへの影響は特に無く、再度クライアントが接続してくれば、また、サービスを
再開することが出来ます。
スレッドモデルで同じことを実装することは可能なのか?
特定のワーカーが何らかの理由で同期シグナルを発生させた場合、その特定のワーカが死ぬのは
しょうがないと思うんですが、他のワーカーまで道連れにしてしまうのは避けたいと思っています。
スレッドモデルを使ってこのような処理を安全に書けないって事は無いんじゃないのって思うんですが、
いかがなもんでしょう?
また、MySQLはマルチスレッドで動いているらしいのですが、こういったDBサーバは更に複数のワーカ間で
データの排他や同期を取る必要があると思うんですが、こういったプログラムは同期シグナルとどうやって
折り合いをつけているんでしょうか。
これがいわゆる茨の道ってやつですか?
133: 93 [sage] 2008/07/25(金) 12:52:01 AAS
>>131
まだ、なにも作ってないですよ。
pthreadというか、マルチスレッドのプログラムを作るのが始めてなので、
いろいろサンプルを作って勉強している最中です。
このスレとっても勉強になります。
レスしてくださっている皆さんありがと。
ところで、本屋行っても、pthread(マルチスレッド)に関する書籍ってほとんどないですよね。
あっても、10年ぐらい前に出版されたものが殆んどで。
この先、CPUはメニィコアに進もうとしているからもっと沢山あっても良いと思うんですが、
pthreadってあんまり使わないんですかね?
135: 93 [sage] 2008/07/25(金) 13:40:09 AAS
>>134
そっか。
やっぱりそうなんですか。
非同期シグナルであれば、シグナル受け専のスレッドを立てておいて、そこで
sigwait()するってのは判るんですが、同期シグナルはsigwait()では待てないですもんね?
ん?待てるのか?
ちょっと試してみる。
でも、待てたとしても、どのスレッドがその同期シグナルを発生させたかって、シグナル受け専
スレッドで判らないけりゃどうしようもないですし。
139: 93 [sage] 2008/07/29(火) 10:32:25 AAS
とりあえず、やってみました。(Solaris10 x86です。)
ボス側で全てのシグナルをブロックし、シグナル受信専用スレッドを作成し、そこでsigwait()。
ワーカースレッドでSGISEGVを発生させるために、NULLアドレスに書き込み。
結果は、プロセスごと終了。
同期シグナルは発生元のスレッドに送られるのでシグナル受信専用スレッドでsigwait()していても
捕まえる事が出来ないってことですね。
同期シグナルは、ワーカースレッド側でsigset()して、シグナルハンドラ側でボスに >>125 すれば、
とりあえずハンドリングは出来ますが、 >>127 にもあるように、どこまで動くのかは不明ですね。
>>134 にもあるように、この辺りがマルチスレッドと、マルチプロセスの差という事なんですね。
そもそもスレッドってなに?、スタックとスレッドの関係って?、プロセスとスレッドの関係って?
OSはスレッドをどう認識してるの?
なんてことが判っている人にとっては自明なんでしょうが、私にもようやくこの辺りが判って来た
様な気がします。
なかなか使いどころが難しいですが、面白い仕組みですね。
上下前次1-新書関写板覧索設栞歴
スレ情報 赤レス抽出 画像レス抽出 歴の未読スレ AAサムネイル
ぬこの手 ぬこTOP 0.021s