スレッド間のコンテキストスイッチってそんなにコストかかるもんなの? ページ変換テーブルとか切替える必要なさそうだし、たいしたコストは ないもんだと思ってた。 スタックが分散するから、キャッシュは無駄になりそうだけど。
キャッシュに関しては、量よりも直接のヒット率の方が影響あるんじゃないかな。 中断したタスクを再開したときに、同じプロセッサが割り当てられるとは限らないわけで 全部読み直しに近い場合もあるだろうし。 ただ、その辺はカーネルのスケジューラが出来る限りのことはしてくれると思うし どの程度差がでるかはわからんね。
> ただ、その辺はカーネルのスケジューラが出来る限りのことはしてくれると思うし Windowsはこれやってくれないんだよねえ…。 重いプロセスをSMPマシンで動かすと、複数CPU間を行き来して余計に遅 くなったりする。 UNIXで、そのへんちゃんと考慮してくるのって、ある?
アフィニティを手動設定して終わりじゃないの?
>>746 せいぜい「便利なときがある」くらいでしょ。
マイクロソフトお得意の場当たり的スケジューラ特殊化だから。
>>759 一つのプロセス(スレッド)を特定のCPUに割り当てたければ、
processor setを使うのがSolaris流儀。
>>749 > ちゃんとアクティブなスレッド数を調整してくれる機能があるって。
何をどう調節するの?
アクティブじゃなくていいかどうかはどう判断するんでしょう?
後回しにされたスレッドの立場は?
>>760 手動で設定しないと上手くいかない状況は、スケジューラが考慮してい
るとは言えないのなの。
>>763 同時に実行される必要がないタスクだからIOCPに管理されたスレッドを使うのであって
後回しにされるタスクが出るのが嫌なら
普通のスレッドを使えばよいだけ。
>>765 要するに、静的に分類して、優先順位をつけるわけですね?
素のWindows + IISで速い例なんてあるの? IBM, NEC辺りのはカーネルが素のWindowsとは違う独自バージョンだよ。 奴等ソースコードいじっているから。Databaseの時もそう。
は?
まあいいや。「とりあえずWindowsを叩いときゃいいや」って人は相手にしないどこ。 説明しても理解する気無さそうだし。 大半の人は「良いところもあれば悪いところもある」ってのは分かってるんだから。
おまいら捨てハンぐらい付けたらいかがでしょうか。 何人いるの?
まず自分からつけなよ。 今後全部読み飛ばすようにするから。
772 :
770 :2005/11/17(木) 08:44:23
あ、全然別の人だったかな。 自分以外で ? を使っている人って事で勘違いしたかも。 だったらごめんね。
773 :
772 :2005/11/17(木) 08:44:59
IOCPが理論的に最速であることを説明してくれる人きぼんぬ
用途の問題だけど。 4CPUとして、 5つの計算をして最後に同期を待つ必要があるのなら 普通のスレッドで並列に実行させれば、1つの計算の1.25倍+αで終わるけど 非同期に1000個のリクエストが来るサーバーでは 最大4つ以上は同時実行せず、1つづつ完全に終了するまで待つ方が オーバーヘッド分効率が良いというだけ。 あと、IOCPを使ったサーバーでも、普通はlistenするスレッドは独自に動いてるし。
タスク処理時間に偏差がある場合、 平均待ち時間が悪くなるスケジューリングポリシーですね。 処理時間が見積もりやすい科学技術計算辺りが得意分野なのでは?
タスク処理時間の偏差はいいですけど タスク開始時間の偏差はいかがですか?
>>777 コンテキストスイッチのオーバーヘッドに比べて、
デメリットの方がずっと大きいような…
# webサーバの場合、客を待たせる時間になっちゃうから。
総処理時間合計のベンチマークではいい性能に見えるんだろうけど…
ネットワークの遅延の方が大きいような。 それ以外にも、例えばnagleが有効ならば数百msの遅延が簡単に発生するわけで。
で、結局は道具の使い方の問題だと思うし。 例えば、秒単位の遅延が見込まれる処理(CGIの実行など)は 別にスレッドを動かして、「実行が終わったよ」というシグナルをポストして 他のリクエストと同等のタスクとして終了処理などをするという手もあるし。 そもそも、計算だけで秒単位の時間がかかる処理をWeb鯖が行うというのは 一般的なのかどうか。 (ディスクI/OやDBとの交信など、スレッドがブロックした場合には 次のタスクの実行が開始される、という仕様だから)
>>769 この板でそんな物説明する奴が悪い。シッシッ!
>>1の想像を越えた良スレになりつつある件について
久々に伸びてると思ったが、 unix板の悪い面が出まくりだな。 windowsアレルギーってまだあるんだ。
windows機雷
IOCPがUNIXで実装されないことについての具体的な答えはなかったな。 実装されないことについての必然的な理由が必要というわけではないけど。
乙
791 :
名無しさん@お腹いっぱい。 :2006/01/20(金) 17:39:54
pthread_createして生成されたスレッドが死んでってのを 何十回か繰り返すとpthread_createからENOMEMが帰ってくるんだけど 何が足りないの? 空きメモリは有るみたいだし,生きてるスレッドは1個だけで RHL9なんすけど.
>>792 >detachかjoinしてる?
791がそれをしてないに、10000 マルク。
794 :
791 :2006/01/23(月) 11:10:36
すっかり忘れてた.
795 :
名無しさん@お腹いっぱい。 :2006/01/26(木) 13:52:32
pthread をソケット通信で試しに使い始めてみたんですが、どうやらリークしてるっぽくて、 mtrace の結果と objdump を元に、glibc のソースから pthread.c や specific.c をつき合わせたら、 int __pthread_initialize_manager(void) の /* Setup stack for thread manager */ __pthread_manager_thread_bos = malloc(THREAD_MANAGER_STACK_SIZE); ↑↑↑↑ こいつが1度だけ呼ばれて 8160 byte かかえたまま free されてないっぽいんです。 また実行中に int __pthread_setspecific(pthread_key_t key, const void * pointer) の void *newp = calloc(PTHREAD_KEY_2NDLEVEL_SIZE, sizeof (void *)); も free されないので、accept の回数を重ねるたびにどんどん膨らんでいきます。 これについて何か既出の情報ってありますか?
796 :
795 :2006/01/26(木) 14:20:07
うおぉぉぉ 真上に解決策が書いてあるじゃまいか orz pthread_detach し忘れてますた スレ汚しすまそ
そういえば、joinでは複数のスレッドを待てませんが、 どこかでデッドロックを避けるためだと読んだことがあるのですが、 具体的には書いてありませんでした。 知ってる人いますか?
>>798 どこだったか思い出せないのですが
「昔はwaitallだかwaitanyの様なのがあった」らしいです。
ゾンビネタを読んで疑問を思い出しました。
<ちら裏> pthread を使ったソケットプログラム。 accpet したソケット fd を子スレッドに void* で渡す、定番のコードのつもりだったんだけど、 void * のポインタなんだからと思って fd=accept(listen, &addr, &addr_len); pthread_create(&id, NULL, child_func, (void *)&fd); と参照渡しをしたところ、同時多数アクセスがあったとき、親スレッドと子スレッドで異なる fd にアクセスする奇怪現象に悩まされた。子スレッドが fd を実際に読むする前に、 親側では次の accept が呼ばれて fd が書き換わってたというオチ。素直に pthread_create(&id, NULL, child_func, (void *) fd); と値渡しにすることで解決。 お粗末。 </ちら裏>
粗末だな……
accept() してから pthread_create() よりも、 「まずは listen() したソケット用意して、 そいつを非ブロックモードに設定して、 同時接続最大数のスレッドを pthread_create() して、 生成されたスレッドそれぞれで mutex 獲得して、 listen ソケットを accept() して、 mutex を解放。 accept() の戻り値が 0 以上だったら、その戻り値を接続相手との通信ソケット記述子に使用する。 通信終了したら accept() の戻り値を close() する。 mutex 獲得に戻る。」 が、いいんじゃないの? ちがうの? いつも、こーゆーふーにしてたよ…
>>802 非ブロックモードにしないでみんなで
acceptでスリープするのはどう?
あとmutexは短時間の排他に最適化されるのでmutex+acceptはまずい。
sem_wait/postか、condvarをつかう。
>>803 そのやり方は、設計に依存するんじゃね?
>>802 おいおい……それ思いっきりビジーループしてるじゃねえか。
却下だ、却下。
>>805 802が暗黙の了解で select/poll してると想像してみるテスト
>>805 >
>>802 > おいおい……それ思いっきりビジーループしてるじゃねえか。
> 却下だ、却下。
mutex の獲得で待ち合わせしててもダメなの?
>>807 mutexをそういう風に使ってはいけないと婆さんが言ってました。
acceptしてそのまま処理するのが普通なの? 私はaccept専用スレッドつくって、acceptしたら別スレッドに処理させるかな。 そしたらacceptスレッドは、接続があるまで(acceptでもselectでも)寝てればいいし。 どうでもいいのなら、1接続1スレッドでスレッド使い捨て、 まじめに作るなら、1スレッドで複数の接続を扱うようにする。 スレッド数はCPU数とかに応じて適当に決める。 でも、みんなでacceptするやり方だと、 acceptすべきかどうか判断するのが簡単になりそう。 自分が上限まで接続を扱ってたら、acceptしなければ良いだけだし。 acceptして放置するのと、acceptしないのってどっちが良いんだろうか…
ヘビーロード、特に接続到着について、サーバは両者の組み合わせ。
>>809 確か、accept()をすると、SynAck返したはず
だから、実行するのとしないのとでは挙動に違いがでるよ。
うん。そこまではわかるんだけど、 いずれ処理してやれるなら、とりあえずacceptした方が良いのかな。
813 :
811 :2006/02/28(火) 22:22:57
>>812 いやだからさ、TCPコネクションにタイムアウトさえしなければ
どっちでもいいと思うぜ。
>>812 listen状態のsocketのbacklogを伸ばす方法もある。
816 :
811 :2006/02/28(火) 23:12:51
>>815 あう。
そうだったね。スマソ > 809
817 :
811 :2006/02/28(火) 23:21:52
といっても、accept()を思い出したように処理してるアプリなんて今まで見た事ないなぁ。
「accept自体は比較的重い処理なので listenしているスレッドではacceptableというイベントのみを検知して accept自体はワーカースレッドに投げて処理させる」 という方式は、どこかで見たよ。 「acceptが重い処理」というのは、たぶんWinsockのIOCPとかAcceptExとかその辺を調べている時に 見たんだと思うけど、accept時のカーネル内部での処理の重さは、 Windowsでもそう変わらないでしょ、たぶん。
acceptが重いんじゃなくて、 acceptをたくさん動かすためにはその分プロセスが必要だからでしょ? Solarisなんかはthread単位で大丈夫だけれど、それでもselect/pollよりリソース食う。 apache2はその辺いろいろカスタマイズできるよね。
なんでacceptとプロセスが関係あるのさ?
>>818 がWindowsの話なら、マルチプロセス型のサーバーなんか絶対あり得ないのに。
>>818 他のOSは知らんけど、Linuxに限って言うとacceptはそんなに重い処理では無いよ。
>>821 どんな OS でもおおむね以下のような処理になると思われる.
受信割り込みから起こされた処理が, コネクション確立を確認
したら, その情報を listen queue につないで, accept してる
奴を起こす
accept は
while (listen queue が 空)
寝る
socket 作って listen queue から取り出した情報を設定(これ
は受信側でやってもいいし...)
該当 socket をプロセスなりファイルディスクリプターなり
に関連付する
上記 socket から peer address 情報引っ張ってきて引数で
指定された場所に書き込む
結局 accept って寝てるだけじゃん
823 :
818 :2006/03/01(水) 23:50:58
>>822 他のOSも似たような実装だと思うけど、
そうすると、818がWindosにおいてacceptが比較的重いと言ってるのが謎。
824 :
823 :2006/03/01(水) 23:52:32
名前欄間違った。 818でなくて、821ね。
>>822 > while (listen queue が 空)
> 寝る
signal_wait(listen_queue);
とevent駆動型になっている
827 :
822 :2006/03/02(木) 08:07:32
>>825 > signal_wait(listen_queue);
やってること(つかセマンティック)はいっしょじゃん.
要は寝てると...
whileで書くとbusy waitみたいだからいくない
>>828 寝るって書いてあるのに busy wait も何もあったもんじゃないような気が...
>>828 >whileで書くとbusy waitみたいだからいくない
それ既成概念にとらわれているよ。
オープンソースなカーネル見ればわかるよ。
init() の main が for(;;); で終わってるやつ?
それなんの奴?FreeBSD?
1) acceptするスレッドは1匹だけ cond varで餌待ちのワーカースレッド多数 2) 多数のスレッドみんながaccept どっちが性能/設計的に良い?
>>833 スレッドが多すぎると (1) の方が不利な希ガス
でも、2)は、1接続が1スレッドを占有する方式の時じゃないと使いにくいでしょ。 細切れタスク(例えばKeep-AliveなHTTPでの1リクエスト等)をスレッドプールに渡す方式にするなら 必然的に1)になるんじゃないかな。
SMP なんかだと, accept する thread 増やすと client から見た 時の応答性能は上がると思われるので, accept する thread は CPU の個数以上/餌待ちワーカースレッド多数ってのが現実的な解 ではないかと, 愚考してみる.
だから、
鯖がACKを返し、それをクライアントが受け取ってからデータを送信、という
ネットワークの往復期間内にacceptが完了するのであれば
応答性なんて全然変わらないと、
>>815 で指摘されてるでしょ。
>>837 いや、ある条件においてはそれが覆されると思う。
それは、複数のNIC and IPを持つサーバの場合だ。
複数NICで複数IPアドレスを割り振ったら、
>>836 のやり方でも良いと思うよ。
その辺はapache2のチューニング報告でよく語られてますよん。 カーネル内HTTPサーバのTUXではどうなのか知りたいところだが、 いいベンチマーク報告が見つからない。
841 :
名無しさん@お腹いっぱい。 :2006/03/07(火) 16:41:21
>>820 昔は acceptしてから子プロセス作って...って感じだったから、それとごっちゃになってるんじゃね。
俺自身が関わる範囲では
>>809 の「私はaccept専用スレッドつくって、acceptしたら別スレッドに処理させるかな」
で十分だ。
>>841 >>昔は acceptしてから子プロセス作って
今だって、ftp、telnet みたいに子プロセスを作るのは普通にある
Webサーバみたいに短時間でコネクションが切れちゃうものは、
スレッドで済ませる場合があるだけ。
あと、
>>820 と
>>841 は accept 後の並列と、accept の並列を勘違いしてる
843 :
名無しさん@お腹いっぱい。 :2006/03/08(水) 10:42:30
>>842 今だって、ftp、telnet みたいに子プロセスを作るのは普通にある
そうだね。
まぁもともとの
>>800 は、ローカル変数 fd をスコープはずれてからもアクセス
しようとしてるっつう並列処理以前の話だがな
あ、違うな。
>>818 はacceptの重さを問題(真偽はともかく)なのに
>>819 がプロセスの重さに勝手に置き換えて、
それを
>>820 が指摘しているだけだな。
linux って clone つかって pthread 実装してるでしょ? PID も違うものが割り当てられる。。。そこまではいい。 #include <pthread.h> void *func(void *arg) { printf("child %d\n",getpid()); while(1); } main() { pthread_t th; printf("parent %d\n",getpid()); pthread_create(&th,NULL,func,NULL); while(1); } これ動かすと、 $ ./sample parent 29220 child 29222 $ /sbin/pidof sample 29222 29221 29220 pid が 3つ見える。29221 の正体はなぞ。なにこれ?
2.6系使いなよ。
>>846 noneNPTLのマネージャースレッドでは?
>>848 dクス
そういうことみたい
$ getconf GNU_LIBPTHREAD_VERSION
linuxthreads-0.10
SIGHUP を受けて設定ファイル等を再読み込みの要求があったときに 全てのスレッドがループの所定の位置(例えば先頭)に戻って来るまで 待つような同期処理はどうしますか? signal ハンドリング用のスレッドを回しておいて、そいつが SIGHUP を 受けると、各ループが作業中確保していた mutex を奪取するというのが 考えられるんですが・・・・
>>850 mutexをとれる保証はないのでrwlockでバリアするのがよいかと。
853 :
名無しさん@お腹いっぱい。 :2006/05/31(水) 23:16:24
stl の string って MTsafe じゃないの? g++ aaa.cpp -I /usr/pkg/include/stlport -L /usr/pkg/lib/ -lstlport_gcc -D_PTHREADS -D__STL_PTHREADS -D_THREAD_SAFE -D_REENTRANT -g using namespace std; list<string> l; pthread_mutex_t mut; void * do_add(void * arg) { printf("thread arg = %s\n", (char*)arg); pthread_mutex_init(&mut, NULL); string aaa = "aaa"; int count_clear = 0; while (1) { int ret = rand() % 2; pthread_mutex_lock(&mut); if (ret) { l.clear(); count_clear++; } l.push_back(aaa); pthread_mutex_unlock(&mut); printf("count_clear=%d\n", count_clear); } return NULL; }
854 :
名無しさん@お腹いっぱい。 :2006/05/31(水) 23:18:12
つづき int main () { pthread_t thread; pthread_create(&thread, NULL, do_add, (void *)"t1"); while (1) { if (!l.empty()) { for (list<string>::iterator it = l.begin(); it != l.end(); it++) { // printf("l->c_str() = %s\n", it->c_str()); assert(strcmp(it->c_str(), "aaa") == 0); } } } return 0; }
そのプログラムって、aaaをいじってないから、 stringがMTsafeかどうか関係ない気がするんだけど、見落としてる?
containerがMT-Safeかどうかと、 containerをいじった時に、既存のiteratorが有効なままかという概念は別。
どういう結果を期待してるのか書いてないけど、 aaaじゃなくてbbbと印字されるんだったらstringが(MT)Safeではないことになるかな。
860 :
名無しさん@お腹いっぱい。 :2006/06/01(木) 11:25:47
>>854 で
main() も l にアクセスするのにどうしてmain内では排他制御してないの?
>>853 mutは何を保護するためのmutexですか。
Goolge Summer of Code 2006で、 lock free C++ containerやるみたいね。
863 :
名無しさん@お腹いっぱい。 :2006/06/02(金) 23:34:46
ああ、ありがとみなさん かいたあと、あれ、 iterator をいじってる最中に、 it がさしている先ってかわる可能性あるなと。。。 >> 857 さんの意見がそのとおりで かきなおしました。 で、main で l をさわっているのに、Mutex してなかった理由は、ここらへんよんでいて The SGI implementation of STL is thread-safe only in the sense that simultaneous accesses to distinct containers are safe, and simultaneous read accesses to to shared containers are safe. If multiple threads access a single container, and at least one thread may potentially write, then the user is responsible for ensuring mutual exclusion between the threads during the container accesses. でなにをとちくるって読んでいたのか。。というと read はあれか、共有していても、だいじょぶなのか?とか英語をまったく よんでない状態でした。。。 2 行目をみればわかるとおり、access してる最中は、user は、 mutual exclusion をやらなきゃならんとかいてあったりしますです。。。 スレよごしてごめんなさいです。
864 :
名無しさん@お腹いっぱい。 :2006/06/02(金) 23:36:29
で、かきなおしました int main () { pthread_t thread; pthread_mutex_init(&mut, NULL); pthread_create(&thread, NULL, do_add, (void *)"t1"); while (1) { pthread_mutex_lock(&mut); if (!l.empty()) { for (list<string>::iterator it = l.begin(); it != l.end(); it++) { // printf("l->c_str() = %s\n", it->c_str()); assert(strcmp(it->c_str(), "aaa") == 0); } } pthread_mutex_unlock(&mut); } return 0; } これで、20 時間くらいまわしても大丈夫でした。 ありがうございました。
ああ、>> 858 さんだおゆるしを。。。
866 :
858 :2006/06/02(金) 23:49:42
なんかおかしいと思ったw
Linux で複数のスレッドから共通に呼ばれることを目的とした自前の syslog 関数 (mysyslog) を作って、 その始まりと終わりで mutex の lock/unlock をやっているのですが、負荷が小さいときは問題が ないのですが、負荷をかけてゆくとなぜか segmentation fault してしまいます。 $ uname -a Linux hoge 2.4.31-0vl1.12smp #1 SMP Mon Dec 26 22:01:47 JST 2005 i686 unknown $ rpm -q glibc glibc-2.3.3-3vl1.3 コアダンプは以下の通りです。 Program terminated with signal 11, Segmentation fault. Reading symbols from /lib/i686/libpthread.so.0...done. Loaded symbols for /lib/i686/libpthread.so.0 Reading symbols from /lib/i686/libc.so.6...done. Loaded symbols for /lib/i686/libc.so.6 Reading symbols from /lib/ld-linux.so.2...done. Loaded symbols for /lib/ld-linux.so.2 Reading symbols from /lib/libnss_files.so.2...done. Loaded symbols for /lib/libnss_files.so.2 #0 0x400258b0 in sem_timedwait () from /lib/i686/libpthread.so.0 (gdb) bt #0 0x400258b0 in sem_timedwait () from /lib/i686/libpthread.so.0 #1 0x400225cc in pthread_mutex_unlock () from /lib/i686/libpthread.so.0 #2 0x08054e50 in mysyslog (priority=7, msgformat=0x805c5c0 "send_proc() unlocking mutex_sleep") at syslog.c:117
ソース貼れば、的確なレスがつくぞ
自前の syslog 関数です。一部省略してます。 syslogfp は別途 myopenlog で開いた状態で呼ばれています。 char hostnmame[MAX]; pthread_mutex_t mutex_log = PTHREAD_MUTEX_INITILIZER; void mysyslog(int priority, const char *msgformat, ... ) { va_list args; char format[MAX], newname[MAX]; time_t tsec; struct tm t; struct stat stat_buf; const char *mon[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; if(syslogfp == NULL) return; /* get time of this call */ tsec=time(NULL); gmtime_r(&tsec,&t); /* filename */ pthread_mutex_lock(&mutex_log); if (priority <= cf.loglevel){ va_start(args, msgformat); sprintf(format,"%s %d %02d:%02d:%02d %s %s[%d]: %s\n", mon[t.tm_mon], t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, hostname, logident, (int)getpid(), msgformat); if(priority <= LOG_INFO){ vfprintf(syslogfp, format, args); fflush(syslogfp); } } pthread_mutex_unlock(&mutex_log); <---- ここで seg. fault return; }
ちなみに呼び出し側 send_proc() の該当箇所は pthread_mutex_lock(&mutex_sleep); sleep_mode = 1; mysyslog(LOG_DEBUG, "send_proc() unlocking mutex_sleep"); <--- 呼び出し行 pthread_mutex_unlock(&mutex_sleep); こんな感じです。mutex のデバッグのために呼び出したログ関数で問題が起こっています。 何か助言頂けると幸いです。
871 :
867 :2006/06/04(日) 17:49:13
ちなみに、seg. fault は違う場所でも発生して、ホスト名解決のためにライブラリ関数を if ( gethostbyname_r(hostname, &host,buf, sizeof(buf), &result, &h_errnop) != 0 ){ のように呼んだ場所でも、mutex 周辺が原因のような segmentation fault を起こします。 引数は全て実体が正常に確保されています。以下、そのときのコア Program terminated with signal 11, Segmentation fault. Reading symbols from /lib/i686/libpthread.so.0...done. Loaded symbols for /lib/i686/libpthread.so.0 Reading symbols from /lib/i686/libc.so.6...done. Loaded symbols for /lib/i686/libc.so.6 Reading symbols from /lib/ld-linux.so.2...done. Loaded symbols for /lib/ld-linux.so.2 Reading symbols from /lib/libnss_files.so.2...done. Loaded symbols for /lib/libnss_files.so.2 #0 0x400259a3 in sem_timedwait () from /lib/i686/libpthread.so.0 (gdb) bt #0 0x400259a3 in sem_timedwait () from /lib/i686/libpthread.so.0 #1 0x400225cc in pthread_mutex_unlock () from /lib/i686/libpthread.so.0 #2 0x431aeb0c in _nss_files_gethostbyname_r () from /lib/libnss_files.so.2 #3 0x4015e45b in gethostbyname_r () from /lib/i686/libc.so.6
872 :
867 :2006/06/04(日) 17:53:35
たびたびすいません。
>>869 の
pthread_mutex_t mutex_log = PTHREAD_MUTEX_INITILIZER;
は
pthread_mutex_t mutex_log = PTHREAD_MUTEX_INITIALIZER;
の転記ミスでした。
va_end()が抜けて無いか? 原因は、負荷を与えた時にスタックが枯渇して落ちてるんじゃないかな? 恐らくgethostbyname_r()も(タイミングが悪いと)その影響で落ちてるのだと思う。 ↓こうしたらどうか? if (priority <= cf.loglevel){ va_start(args, msgformat); sprintf(format,"%s %d %02d:%02d:%02d %s %s[%d]: %s\n", mon[t.tm_mon], t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, hostname, logident, (int)getpid(), msgformat); if(priority <= LOG_INFO){ vfprintf(syslogfp, format, args); fflush(syslogfp); } va_end(args); <ーこれが抜けてる }
874 :
873 :2006/06/04(日) 19:01:27
ん?? それ以前に可変個引数の使用方法がおかしいぞ。
875 :
867 :2006/06/05(月) 10:22:43
>>873-874 指摘ありがとうございます!
sem_timedwait とかに気をとられて glibc のソース解読とかを始めそうになってましたが
普通の処理でコードが間違ってそうですね・・
なんだか pthread そのものの問題じゃなさそうですが、解決したらまた報告します!
>867 MAXが足りてないオチ。 だから、n付き関数呼べとあれほど(ry
877 :
867 :2006/06/06(火) 23:07:34
867 です。解決報告をしたいところだったんですが、指摘して頂いた点に留意してコードを以下のように変えても依然同じ場所 (sem_timedwait) で seg. fault します。 backtrace で変数の中身をを見ても、ここでは MAX があふれてるとかの問題は起きていません。あと、ulimit も全て unlimited にしてみましたが、状況は変わっていません。 void mysyslog(int priority, const char *format, ... ) { va_list ap; char msg[MAX], buf[MAX]; time_t tsec; struct tm t; const char *mon[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; if(syslogfp == NULL) return; tsec=time(NULL); gmtime_r(&tsec,&t); pthread_mutex_lock(&mutex_log); va_start(ap, format); vsnprintf(buf, MAX, format, ap); va_end(ap); snprintf(msg, MAX, "%s %d %02d:%02d:%02d %s %s[%d]: %s\n", mon[t.tm_mon], t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, hostname, logident, (int)getpid(), buf); if(priority <= LOG_INFO){ fprintf(syslogfp, "%s", msg); fflush(syslogfp); } pthread_mutex_unlock(&mutex_log); return; }
878 :
873 :2006/06/07(水) 02:38:36
879 :
867 :2006/06/07(水) 02:51:41
>>878 ありがとうございます!
色々ツールの必要性を感じてたとこなのでこの手の情報はとても助かります!
>>873 >>876 867 です。
全てのコードをお見せできるわけじゃないので表現しづらいのですが、
valgrind を使ったところ、コード貼ったのとは違う場所のソケット通信の部分で
points to uninitialized buffer と出てきたので、送信バッファを隈なく
初期化するようにしたら症状が治まりました。裏で何が起きてるのか
まだ把握してませんが、スタックが侵食されてたのかもしれません。
結局のところ pthread とは全く関係ない部分の問題でスレ汚ししてしまいましたが、
おかげ様で解決しました!ほんとう、助かりました!
たまたま症状が出にくくなっただけで、問題が解決されたわけではない気がするのは、気のせいだろうか。
882 :
873 :2006/06/07(水) 15:03:17
>>880 それはよかった。
けど、
>裏で何が起きてるのかまだ把握してませんが、スタックが侵食されてたのかもしれません。
今後の為にも、ちゃんと把握しておいた方がいいぞw
その送信バッファの近所でオーバーランしてる予感
884 :
867 :2006/06/07(水) 16:13:04
Linux 以外にしたら解決したりして。
>>886 流し読みした限りをめちゃくちゃ乱暴に要約かつ意訳すると、
rwatoson: libpthreadにあってlibthrにない機能はもうないし、現実のアプリ
ケーションはlinuxのスレッドモデルをみんな基本にしてるし、でのベンチ
マークも上だから変更しようよ。
deischen: libpthreadにあってlibthrにない機能を追加してくれたらね。1:1モ
デルじゃ難しいだろーけど。
jb: libpthreadのその機能とかはi386以外のアーキテクチャで動いてんのかよ。
deischen: 知らない。POSIX標準に適合させたいだけだよ。他のアーキテクチャ
でのlibpthread向けの作業は誰かやって。
davidxu(libthrの作者): (最初のrwatosonメールに)賛成。デフォルトにしよう。
jb: (deischenに対して)ほとんどのlinux開発者(アプリを含むと思われる)は
たぶん、POSIXが何なのかすら知らねーよ。
devidxu: (deischenの最初のメールに対して)あんたの言うようにsystem scope
threadをlibpthreadに追加すべきじゃないと思うし(kernelの修正とかしなきゃ
現実的じゃない)、そんな機能は削除、削除。
それに、そんな方法だと現実の世界のほとんどのアプリケーション性能を改善
するためにhard realtimeが必要にだね。hard realtimeが必要ならなんで
FreeBSDを使ってんの?
あとさ、最近のCPUフレンドリーなスケジューラをユーザランド上に導入するの
って簡単じゃないと思うし。
(1:1モデルじゃ難しいと言われたことに対して)不可能じゃないし、少しずつや
ってるよ。
deischen: (devidxuに対する返信)(system scope threadに関して)system scopeじゃないよ。 (ユーザランド上のスケジューラについて)KSEが良き計らってくれるから 影響ないね。 以下 devidxuとdeischenの既に対応してるとか、hardworkだとか、その機能 はどこの担当だとかが続く中で、rwatsonのaceept, i/o, i/o, i/o, ..., close とか、kip.macy(sun4vのport作業担当)がKSEの移植でグチったり、 Sunのエ ンジニアがscheduler activationsやめたのは重要なアプリケーションはみん なlinux方式を仮定してるからだけど、誰かFreeBSD方式ととlinux方式のちゃ んとした比較教えてとかいったり、julianのKSE講座があったり、peterが bike_schedという名前はこのスレッドのことじゃないよとかいいながら perforceでやってるスケジューラ周りのpatchを提出してるってことでOK? そのうちMLで結論でたら誰か報告おねがい。 俺の結論としては世の中みんなLinuxに振り回されっぱなし。 いまさらながらPOSIXの存在価値っていったい……。
Linux vs POSIXじゃなくて、1:1 vs M:Nでしょ? > 現実のアプリケーションはlinuxのスレッドモデルをみんな基本にしてるし こういう発言が誤解を招くのだろうが。 しかもdefaultを1:1にしようって話で、 M:Nは金輪際辞めろっていうわけでもないし。 上手く両方共存していって欲しいが、人的リソースがきついかな。
すまん、誤解させたようだ。結論と書いてある部分は無視してくれ。 その部分は、単になんでもLinuxが採用したものが標準なっていくっ てグチろうとした部分の削除し忘れだ。 それとも、その部分以外でLinux vs FreeBSDに読めた? 徹夜明けで頭はたらいていないんで、そーなら、なおさらごめん。
891 :
名無しさん@お腹いっぱい。 :2006/07/19(水) 16:44:29
すいません。プログラム板の「マルチスレッドプログラミング相談室」から引っ越して来ました。
質問内容とこれまでの議論はこちらを見てください。
http://pc8.2ch.net/test/read.cgi/tech/1130984585/627-659 Linux での pthread と accept() の動作についてなんですが、分かる人居ますか?
(このスレの上の方で近いことが既に議論されていたようですが)
一応一つのスレッドでだけ accept() をして続きの処理は待機させておいた
スレッドにやらせるという方法で回避できたので現在は困ってはいません。
現在は何故そうなるのかという疑問だけが残った状態です。
> 現在は何故そうなるのかという疑問だけが残った状態です。 Linux板の方がいいんじゃないの?
>>892 それってpthreadは無関係ってこと?
894 :
891 :2006/07/19(水) 17:53:50
>>892 そうですかね。
Linux と pthread とネットワーク全てに絡む話なので、何ともいえない
ところなんですが。(やっぱり Linux の pthread 実装がクソだったという
結論になる可能性もないわけではないですが…)。
ところで Linux ではなく UNIX (Solaris) とかは複数スレッドからの
accept() は問題無く動くんでしょうか?
(なんとなく動きそうな気はしますが)
>>891 現実逃避をかねて実験してみた. FreeBSD でやると別の fd 返してくる.
手元に Linux ないんで, 動作の比較はできなかったけど...
スレッド起こす側は下みたいな感じでいいんだろ?
main(...)
{
...
k = socket(PF_INET, SOCK_STREAM, 0);
// bind/listen する
// ioctl なり fcntl なりで nonblock にする
pthread_create(&t0, 0, thr, (void *)k);
pthread_create(&t1, 0, thr, (void *)k);
}
896 :
891 :2006/07/19(水) 19:59:00
>>895 そうです。そんな感じです。
そうですか。やはりうまく行きますか。
(やっぱLinux板行った方がいいかな・・・)
原因がLinuxのバグだとしても、作りは相当変だよ。 だいたいnon-blockingにして闇雲にポーリングするなんてどうなのよ。 1個のスレッドでイベントのあるときだけaccept()するだろ普通。
NPTLなのか?
linux-2.6.17のコード見てみたけど、fdの割り当てはfd table共有してる限り、 同一のspin_lockで排他してるからだぶることなさそうなんだがなぁ。 accept()で返されてくる、第2引数のsockaddr_in(かな?)の内容まで一緒なのか 知りたいかも。 つーか、スレッド使うならソケットをnonblocking modeにしなくていいと思うんだけど、 blocking mode使わない理由があるのかな。
900 :
891 :2006/07/19(水) 22:58:10
すいません。単純化したプログラムを1から作りなおして Fedora Core 5
(Kernel 2.6.17-1.2145_FC5, gcc 4.1.1) でやってみたらちゃんと違うファイル
ディスクリプタを返して来ました。
ということで、これは単純に私のプログラムのバグの可能性も出てきました。
申し訳ありませんが明日プログラムを確認してみます。(今手元になくて確認
できないため)。
おさがわせしました。ということでまた明日。
>>899 Nonblocking にした理由は元のプログラムがシグナルを別スレッドで受けていて、
それによってフラグを変更してループを抜けるということをしていたので停止
させたくなかったということです。そういうのがなければ block しても同じですね。
>>900 プログラム板の方にもフィードバックお願いします。
902 :
891 :2006/07/20(木) 16:01:45
申し訳ありません。私がバグっておりました。 accept() 後の処理を別関数でやっていたのですが、その中で fdopen() を使って accept() で受け取ったファイルディスク リプタから FILE * を作って fgets() や fprintf() を使って いました。それでその関数から返る直前に fclose() をして いますが、そうすると当然元となったソケットも close() されます。 このことをすっかり忘れていたためこの別関数から戻って 来ても accept() で取ったファイルディスクリプタは オープンしたままだと思い込み、別関数から帰って来てから close() までの間に別スレッドで accept() した時の ファイルディスクリプタと同じ値になることがあったため、 今回の疑問に繋がっていました。 ということでお騒がせしました。 Linux でも複数スレッドからの accept() は問題無くできます。
903 :
891 :2006/07/20(木) 16:02:16
904 :
:2006/07/21(金) 21:15:16
またまたご冗談を(AA略
>>902 亀レスな上に蛇足かもしれないけど、環境によって同時Acceptは挙動が
違うらしいから排他したほうがいいと何かで呼んだ。
906 :
名無しさん@お腹いっぱい。 :2006/08/15(火) 19:11:59
悪くないけどちょっと古い。
別に古くはないけど、 銀行ATMの例に沿って進む例題形式だから、 それだけじゃ細かいことまでは分からないね。 それを通読した後、 David R. Butenhof "Programming With Posix Threads" (翻訳が出てるが酷いらしい) スティーブ・クライマン「実践マルチスレッドプログラミング」 ビル・ルイス「Pスレッドプログラミング」 ダグ・リー「Javaスレッドプログラミング−並列オブジェクト指向プログラミングの設計原理」 辺りを読めば? Java関係ないじゃんと思うかもしれないが、 この本が優れていることは読めば分かる。 pthreadでしかプログラミングしなくても、 設計面の話なので大いに参考になる。
>>910 それは立ち読みして自分にはまだ難しかったので買ってないです
>>910 キューや共有メモリなどのプロセス間通信の勉強に<Vol.2>は良いですか?
>>908 スティーブ・クライマン「実践マルチスレッドプログラミング」
これは入門者の一冊目として適してますか?
ビル・ルイス「Pスレッドプログラミング」
は廃刊でした。
>>908 に挙げたのは、
「通読した後」と書いたように入門用じゃないよ。
入門は
>>906 でいいんじゃない?
けど読めると思えば、一冊目に、
> スティーブ・クライマン「実践マルチスレッドプログラミング」
でいいと思うけど、pthreadの細かい仕様は、マニュアルを読むことになる。
これは急所どころだけ書いてある。他のスレッドと比べたりして。
とりあえず
>>906 が分かりやすそうだったので買いました。
これでできるところまで覚えてからクライマンのを読んで見ます。
>>911 や、例にあがっているコードをほぼそのまま使っても動く
ものができてしまう上に、OSの違いによる挙動の違いや、コード
そのものの解説が付いているから、難しいというよりは身体で覚える
タイプの本だ、と個人的には思っている。
>>912 Vol.2は残念ながらあまり見ていない。必要な人には良いとは思うが。
917 :
名無しさん@お腹いっぱい。 :2006/09/07(木) 13:25:27
マルチスレッドのプロセスでforkするのって色々難しいですわあ
するな、が正解。fork&execならともかく。
919 :
名無しさん@お腹いっぱい。 :2006/09/08(金) 08:22:13
スレッドを作ってからfork&execするのがやっかいなんだあ
プチfork爆弾期待age
921 :
名無しさん@お腹いっぱい。 :2006/09/10(日) 18:58:51
質問です。 プロセス内のスレッドはグローバル変数を共有してますが、pthread_createの 最後の引数で渡した変数しかスレッドのルーチンでは読み書きできないんですか? それとも引数で渡さなくてもグローバル変数はアクセスできるのでしょうか?
922 :
名無しさん@お腹いっぱい。 :2006/09/10(日) 19:09:47
自己解決しました。引数で渡さなくてもアクセスできました。 なぜ、ポインタ引数を一つ渡せるような仕様になってるのでしょうか? 必要ないと思うのですが。
スレッドが複数あったら?
924 :
名無しさん@お腹いっぱい。 :2006/09/10(日) 19:26:23
>>923 複数あってもすべてのスレッドからグローバル変数はアクセスできるので、
わざわざ引数を設ける必要が無いような気がします。。
知識たらなくてすみません。
( ゚д゚)ポカーン
>>924 知ってる人なら、「スレッドとは」と小一時間話したいけど、
知らない人だと、どんな知識をどれだけ持ってるか分からないから、困る。
>>926 ご教示お願いします、ぜひとも。
入門書は読みました。
>>927 お前の氏素性がわからなきゃ無理って書いてるのに
教えてくれって言うのは、変じゃねーの。
住所氏名性別、学歴、職歴、毎日考えてる事、全部晒してくれたら
ニヤニヤする。
930 :
名無しさん@お腹いっぱい。 :2006/09/10(日) 20:11:43
2ちゃんはカスの集まりなんで、まともに解説できる奴あいねーよ
といえば教えてもらえると思ってもムダです
>>922 深遠な理由があるわけではない。
あった方がグローバル変数だの条件変数だのを使わなくて済む場合が多いから。
でも、こう書くと次は
>>800 をやらかすだろうから、
とりあえずmallocしたポインタを渡すクセを付けておけ。
>>932 ご教示ありがとうございます。
いろいろ考えられてそうなってるわけですね。
>>922 オブジェクト指向言語(たとえばJava)でスレッドを扱う場合は
大体こんな感じになる。
public class Work implements Runnable {
private String name;
public Work(String name) {
this.name = name;
}
public void run() {
System.out.println("My name is " + name + ".");
}
public static void main(String[] args) {
Work work1 = new Work("Tom");
Work work2 = new Work("Mark");
new Thread(work1).start();
new Thread(work2).start();
}
}
このように、スレッドのエントリポイント(runメソッド)は同じでも、
処理を行うインスタンスが異なっている(work1とwork2)ために、
それぞれのスレッドが別々の処理を行うことができる。
で、pthread APIでも同様に、スレッドのエントリポイント(pthread_createの第3引数)
のほかに、スレッド処理の主体となるデータ(すなわちOOで言う「オブジェクト」)への
参照を渡せるようにしている(pthread_createの第4引数)というわけだ。
コールバック系のAPIをはじめとして、こういう任意のポインタを渡せるようように
なっているAPIは多いが、これらも処理の主体となるデータの受渡しに用いられ、
"opaque pointer"などと呼ばれる。
>>934 なるほど。あえてopaqueなポインタになってるけれども、C++などでは
オブジェクトの参照渡しなど重要になってくるんですね。
ありがとうございました。
・・・
・−・
いやいや、グローバル変数で頑張ろうよ
>>935 は会社で先輩に教えてもらう時に、
途中で生悟りしないでちゃんと不明点が無くなるまで
聞けよ? でないと、迷惑掛けるぞ
量産型バグ 対 シャア専用バグ
グローバル変数でも、読むだけなら良いじゃん。
volatileつければ排他も要らないしw
volatileで排他できるわけないしw
945 :
初心者 :2006/10/27(金) 22:08:21
スレッドを無限ループにしたままで、mainを終了させたらリソースは解放されないで 残るのですか?その場合detached属性つけてスレッド生成すればよい?
947 :
946 :2006/10/28(土) 00:02:39
# アンカミスった。無視してくれ
948 :
混沌 :2006/10/28(土) 03:50:29
そろそろ1000レスだな〜〜
捨てると決まったわけじゃないよ。
950 :
945 :2006/10/28(土) 08:06:02
>>945 プロセスが終了したとき、そのプロセスが所有するリソースは全て
OS側に返される。
この原則はマルチスレッドで動作しているプロセスでも同じ。
952 :
名無しさん@お腹いっぱい。 :2006/10/29(日) 08:26:55
>>951 ありがたや〜
同期を取る必要の無いスレッド同士だからデタッチにすべきだと思った。
デタッチするとスレッドの戻り値とかをOSが管理しなくていいから少し
は効率よくなるはず?ジョイナブルでも正常にリソースが解放されるのなら、
そもそもデタッチスレッドって何のためにあるの?
joinするのも只じゃないってことじゃね?
954 :
名無しさん@お腹いっぱい。 :2006/10/29(日) 14:05:29
>>952 デタッチなスレッドを使用する意味・・(私の場合です)
やっぱり、同期は必要になります!?。例えば親子関係のスレッドで、
子供)処理を終えると親に通知する。(mutexと条件変数を使って)
親 )通知を受けるが常時待機はしていない。(別な処理をしている)
こんな関係だと、もし親がjoinで待ってると別な処理が出来ない。この
待機時間がロスタイムになる。
最後に親が死ぬ時は、子供状態を記す管理簿の内容で、
待機するか、即死ねる判断ができる。
これを、ある社会生活をモデルにすると・・・
2時間飲み放題(バイキング)とします。
店側(親)、客(子)にします。
客)自由に飲み食いします。(酔っぱらって時間の事忘れるかもしれません)
店)客の利用時間を監視しつつ、料理の準備や雑用をします。
そして、2時間経過したら 客に帰れ[pthread_kill(客、強制(9))]を
通知します。もちろん、2時間以内に帰ってくれる客もいます。
# 客の意志を尊重しjoinで待ってると、経営が成り立ちません。ってか?
955 :
名無しさん@お腹いっぱい。 :2006/10/29(日) 17:14:09
>>953 >>954 大作やなあ
つまり第一にjoinはブロックされるから使用したくない。
でも、親子関係のスレッドで
>>954 のような処理をする場合でも
あえてデタッチスレッドにしなければならないのだろうか?という疑問が沸いた
ジョイナブルなままでもいい気がするんだけど、。
まあ精進しますわ
956 :
名無しさん@お腹いっぱい。 :2006/10/29(日) 20:20:03
>>955 954です。
要求に合った動作ができれば、それでいいと思います。
しかし、コストパフォーマンスを考慮すると
そうも行かない場合があります。
joinで待つ間、L2キャッシュを占有されてると迷惑な場合も
あります。待つのもね。
だから、用途次第だと思います。
> joinで待つ間、L2キャッシュを占有されてると迷惑 解る人居る?
>957 まったく. 意味が掴めない. 詳しくお願いします.
まさか joinで待つ=対象スレッドが終了するのをポーリングして監視 だと思ってるんじゃあるまいか ごめん、スルーできなかった
961 :
名無しさん@お腹いっぱい。 :2006/10/31(火) 22:43:00
「スルー力」って書き込み自体をしないのが本質だと思います。
何かの記事を真に受けた伝道師みたいに思えます。
>>960 そのポーリングは無意味で、joinの方がましですよね。
・・・
スレッドに対して、要求する処理が終了して〜スレッド消滅までの時間
正確に測る方法が知りたいんですが、だれか知りませんか?
> joinで待つ間、L2キャッシュを占有されてると迷惑
の意味を教えてくれ
>>960 じゃないんだよな?
963 :
名無しさん@お腹いっぱい。 :2006/11/02(木) 23:03:12
>>962 L2キャッシュ・・・
CPUを基点に、データ保存の媒体として、L1,L2キャッシュ(CPU内部)、
メモリ、HDD等とあります。
データへのアクセス時間が最速なのは、前者から順になります。
そこで、A=A+1 と足し算したい場合、Aはどこに保存されてると
早く計算できると思いますか?
はい、レジスタ!
スルーとしけばよかった(>_<)反省
966 :
名無しさん@お腹いっぱい。 :2006/11/03(金) 06:11:44
で?
俺はYahoo!のブリーフケースに保存している。
ブリーフへのアクセス時間が最速、それはブリーフケース
ブリーフケース、ベリーフケース、ベリーファスト! なるほど
970 :
名無しさん@お腹いっぱい。 :2006/11/03(金) 18:03:19
↑笑えるポイントは?
三段活用だろ
972 :
名無しさん@お腹いっぱい。 :2006/11/04(土) 00:10:07
↑解説ありがと。 笑えないけど、笑ってあげよう。
以下は、sem_wait/sem_post を使って同時に 2 スレッドまでしか 走行できないように排他処理を行うテストコード。 これを linux の gdb(6.4.90 系) で実行すると、なぜか assert に 引っかかってしまいます・・・。 debian etch でダメ。なぜか vine4.0 だと問題なし。 #include <stdio.h> #include <pthread.h> #include <semaphore.h> #define MAX_THREAD_NUM (2) #define THREAD_NUM (8) sem_t sem; void thread_func(void *arg) { int id = (int)arg; int ret = sem_wait(&sem); assert( ret == 0 );/* gdb だと謎エラー */ printf("%d start.\n", id); sleep(1); printf("%d finish.\n", id); sem_post(&sem); } int main() { int i; pthread_t handle[THREAD_NUM]; sem_init(&sem, 0, MAX_THREAD_NUM); for (i = 0; i < THREAD_NUM; ++i){ pthread_create(&handle[i], NULL, (void *)thread_func, (void *)i); } for (i = 0; i < THREAD_NUM; ++i){ pthread_join(handle[i], NULL); } sem_destroy(&sem); return 0; }
975 :
974 :2006/12/12(火) 02:55:22
debian etch 、vine4.0 ともに gdb のバージョンは 6.4.90。 ということは libc のバージョンの差が原因なのか・・・。 なんにせよ、gdb 上でセマフォがさっぱり機能しないというのは つらいです。
謎つうかerrnoは見ないの?
sem_initの戻り値をまず確認しないと。
>>974 これが原因じゃないような気はするけど、念のため、
sleep(1)使うのやめてpoll(NULL, 0, 1000)にしてみたら?
あとは
>>976 に同意。
「あとは」じゃなくて、まずerrno。
細かい日本語のつっこみくらいしかできない人乙
981 :
974 :2006/12/13(水) 01:31:19
sem_init() の戻り値は 0(=正常終了)です。 sem_wait() の戻り値は 4(=EINTR)でした。 回避方法が判明しました。 sem_wait() が EINTR で失敗した場合 while ループで リトライする仕掛けにすると良いようです。
Wait中になにかSignal来てんだろ。つか普通するだろEINTR対策
SIGTRAPとか
write()でエラー確認せず文句言うヒトもいます
全てのsystemcallにEINTRチェックを入れるのはしんどい。
>>980 >>984 あれって単なるサンプルコードじゃないの?どうでもいいけど。
サンプルコードに必死になるのはばかばかしいと思って。
988 :
名無しさん@お腹いっぱい。 :2006/12/17(日) 17:04:02
writeしながらwriteしたいんですけど、誰がwriteですか?
埋め
お尻がウンコ臭い
次スレは立てますか?
埋めないと立てますよ
UNIX板に一つくらいpthreadのスレがあってもいいんじゃないかしらん? 無理に他に集約する必要もないでしょう。
無理に分散する必要もないよ。
分散は嫌だ
乙
乙
1000 :
名無しさん@お腹いっぱい。 :2006/12/21(木) 18:00:37
遂に念願の1000GET
1001 :
1001 :
Over 1000 Thread このスレッドは1000を超えました。 もう書けないので、新しいスレッドを立ててくださいです。。。