マルチスレッドプログ ..
513:506
06/06/10 21:59:59
>>511
ご返信をありがとうございます。
これは「多重セマフォ」の解説(使用例の項)でしょうか?
残念ですが、今回の件は同時参照を全て止める必要があるため、初期値を大きくすることに
よって解決とすることは出来ないと思います。情報をありがとうございました。
>>512
ご返信をありがとうございます。
不要と判断していました。たとえばですが。
共有メモリにある情報をまとめたテーブルがあるとします。
関数 SetData01() はこのテーブルの特定のデータを変更します。
同様に SetData02() など複数の関数があり、それぞれ関数内部でデータを変更するための
(受取手にとってわかりやすい形から格納に適した形への変換などの)処理を実施しています。
これら関数には対応する GetData**() も存在し、変更/参照時には共に排他制御(sem2)を実施します。
更に上記個別の値をまとめて取り扱う関数として SetGroupData() なる関数があり、
先の個別にデータを変更/参照する関数を内部で使用します。
この関数でのデータの変更時には、関連する全てのデータ更新が終了するまではデータ間での
矛盾発生を防ぐために他からのデータ参照を止めたい場合があるため、この関数の実行時にも
排他(sem1)を掛けたいと考えています。
回避策として、SetGroupData() で SetData01() などを呼ばずに自前で個別データの
設定を行う方法もあるのですが、少々煩雑な処理をするところもあるため、可能であれば
SetData01() などを利用する形に出来ればと思っています。
そのままのことは書けないため、説明が足りないところもあるかと思いますがご容赦ください。
514:デフォルトの名無しさん
06/06/10 23:56:28
>>513
そのよくわからんけど、SetDataなんちゃらっていうメソッドをいくつも用意するの?
そんなことするよりもさ
SetData(class data)を用意してこいつが責任もって共有データを単独で更新すればいいんじゃないのか?
じゃあ、みんなで同時に呼べねーべバーカとか思うなら、このSetDataは要求をスタックに貯めて逐次実行する
仕組みだけを排他制御で実装すればいいよね。共有メモリに置く程度のデータならそれぐらいで間に合わないかな?
515:506
06/06/11 15:27:03
>>514
ご返信、ありがとうございます。
初めからそういう方向性を考えていれば...と後悔しているところです。(汗
次こそわ。
516:デフォルトの名無しさん
06/06/11 20:23:12
いや、変えないと今のも終わらないから。
517:デフォルトの名無しさん
06/06/12 17:52:57
質問です。
_beginThreadを使ってスレッドをつく、その中でゲームの描画をまわしているのですが
void ThreadMain(void* pParam)
{
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
DWORD dwLastTime = timeGetTime();
while(!pApp->m_bStopThread)
{
WaitForSingleObject(hEvent, 16 - __min(16, timeGetTime() - dwLastTime));
dwLastTime = timeGetTime();
//実際はここで描画をするけど、今は空.
}
CloseHandle(hEvent);
_endthread();
}
こんなことを行なうとタスクマネージャー上でCPUパワー使用率が100%近くになってしまいます。
(WaitForSingleObjectは、待機中CPUパワーを使わないとあったので期待したのですが)
Sleepあたりを入れてみたりしても、Sleep(1000);くらい大きく指定しないと使用率0%付近になりません。
スレッドを使った場合、CPU使用率は高くなってしまうものなのでしょうか?
今まではスレッドを使わず、WinMainで処理をしていたのですが、そちらでは使用率が0%に近かったです。
518:デフォルトの名無しさん
06/06/12 17:53:43
今までスレッドをやらないパターンですとこんな感じでした。
CPU使用率は常に0%付近でした。
//WinMainに
while(TRUE){
while(0 != PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)){
if (!GetMessage(&msg, NULL, 0, 0))
return msg.wParam;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else if (16 <= timeGetTime() - dwLastTime){
dwLastTime = timeGetTime();
// 処理
}
}
519:デフォルトの名無しさん
06/06/12 18:04:20
>>517
それ、WaitForSingleObject で待ってない(即リターンしてる)から。
他のスレッドが所有権を持ってないので、呼び出したスレッドが即座に所有権を得ておしまい。
2度目以降の呼び出しは、呼び出したスレッドがすでに所有権を持っているので即座に完了。
他のスレッドに所有権を持たせておくか、selectとかSleepでも使うべし。
520:519
06/06/12 19:39:57
今思いついたんだけど、とにかくタイムアウトさせたいなら
GetCurrentThread() で得たハンドルを wait してもいいのかもしれない。
521:デフォルトの名無しさん
06/06/12 19:45:59
>>519
アドバイスありがとうございます。
ただ、デバッガで追ってみましたところきちんと毎回Waitしている様子です。
Wait時間を分かりやすく5秒にしてみたところ、私の期待通りの動作をしているようです。
selectについてもこれから試してみたいと思います。
522:デフォルトの名無しさん
06/06/12 19:53:00
Sleep、WaitForSingleObject、いずれも、寝ている時間の精度自体は5msも保証できない
523:デフォルトの名無しさん
06/06/12 19:57:04
>>522
なんと、そうなのですか?
やはりFPS60なんていう精度を望む場合
Waitなんて無しでぶんまわしつづけるしかないのでしょうか?
(そもそも、Windowsで精密ゲームを作ること自体、Windowsの設計理念とは反している気がしますが)
524:519
06/06/12 20:25:43
>>521
そですか・・・ところでメインスレッド側は PeekMessage 無しに戻しましたか?
単純に >>518 から else 以降を取ってしまうと、CPU 100% のビジーループに
なるわけですが。
525:デフォルトの名無しさん
06/06/12 21:08:55
ところで、なんで、NOREMOVEでPEEKしたあと、またGetMessage呼んでるの?
単純に、直接REMOVEでPEEKして、Translate,Dispatchすればいいと思うんだけど
526:デフォルトの名無しさん
06/06/13 14:46:50
pthread_create() で作ったスレッドが終了したかどうかの確認は
どうしたらいいんでしょうか? pthread_join() してしまうとその
スレッドが終わるまで待たされてしまいますよね?
やりたいことは、複数のスレッドを作って、作った側で pthread_t
の配列にスレッド識別子を入れておき、それぞれがバラバラの
時間で終わるんですが、作った側で pthread_t の配列に入っている
値を元にそれぞれが終わったかどうかを確認して、終わっていたら
それに対して pthread_join() をやって資源開放をて、配列の側も
その部分を終了しているという値(たとえば0)にしたいんです。
(つまり UNIX で複数 fork() したあとで waitpid() の WNOHANG
みたいにして何が終わったか、あるいは何も終わっていないのかを
確認するのと同じことです)。こういうのは pthread ではどうやる
んでしょうか?
527:デフォルトの名無しさん
06/06/13 21:33:23
pthreadは詳しくないけど自分でフラグ作っても大して開発効率落ちないと思う。
528:デフォルトの名無しさん
06/06/14 00:10:40
SIG_CHLDシグナルで判断しろよ
529:デフォルトの名無しさん
06/06/14 12:15:21
>>528
pthread で作ったスレッドに対して SIGCHLD は使えないのでは?
子プロセスじゃないんだし。(Linux の実装だと使えるとか?)
530:デフォルトの名無しさん
06/06/14 13:11:53
POSIX P1003.1cにpthread_kill()ってのがあって、
signalを指定する引数に0を指定すると、
pthreadがいれば成功(シグナルは何も送られない)、
いなければESRCHのエラーになる。
processに対するkill(2)と同じ仕様。
> 自分でフラグ作っても大して開発効率落ちないと思う。
ではありますが。
531:デフォルトの名無しさん
06/06/14 13:23:26
>>530
あー! その手があったか。
くっそー。 pthread_kill() の man ページは見ていたのに気が付かなかった。
どうもありがとうございます。
532:デフォルトの名無しさん
06/06/17 17:46:23
スレッドのデバッグって何使ってますか?
Linuxだとgdbだけかな?
533:デフォルトの名無しさん
06/06/19 00:32:10
知恵と勇気
534:デフォルトの名無しさん
06/06/19 01:05:12
>>533
そんなのいらねーよ何か教えねーと食うぞ?
535:デフォルトの名無しさん
06/06/19 10:33:24
「ちびくろ! おまえを たべちゃうぞ!」と、とらは いいました。
536:デフォルトの名無しさん
06/06/19 21:49:53
>>532
かん
537:デフォルトの名無しさん
06/06/20 13:18:17
>>532
おれ、この前 gdb でやってみて大混乱。
使い方調べてから使わないといかんね。
538:デフォルトの名無しさん
06/06/24 17:19:02
無茶しやがって…
539:デフォルトの名無しさん
06/06/24 21:26:39
Windowsで、タスク内の複数スレッド間だけで通用する、高速な同期オブジェクトって無いですかね?
なんか、どれもこれも、重そうで。
540:デフォルトの名無しさん
06/06/24 23:41:41
イベント系はユーザーモードで実行される。
イベント系はプロセス間の動機にも使えるという強みがある。
クリティカルセクションのみカーネルモードで実行される。
クリティカルセクションはプロセス間の動機で使えないがイベント系に比べると
非常に高速だという強みがある
541:デフォルトの名無しさん
06/06/24 23:49:15
>>540
クリティカルセクションって何?
542:デフォルトの名無しさん
06/06/24 23:58:44
ミューテックスとほぼ同じ。
タイムアウト指定ができなかったりする。
543:デフォルトの名無しさん
06/06/25 00:07:28
カーネルモードに移行するものが高速で
ユーザーモードのまま実行できるものの方が遅いって?
544:539
06/06/25 03:10:41
クリティカルセクションだと、スレッド間での排他は楽だけど、同期には使えないよね。
イベント系はカーネルまで落ちるし、プロセス間で使えるから重いよね。
同期に使えて、カーネルまで落ちなくても良いようなヤツが欲しいンだけど、
Windowsでは用意されてない気がするんだ。
それって、正しいのかな?
545:デフォルトの名無しさん
06/06/25 03:29:24
2つのスレッドだけでいいのならクリティカルセクションでも同期は取れる。
最初にどちらかのスレッドが所有権を保持するようにする。
片方のスレッドはEnter.....()で所有権が解放されるまで待機。
所有権を持っていたスレッドが所有権を解放すれば片方のスレッドは
実行が開始されるようになる。
高速性が大事でかつ簡易な同期処理でいいならこんな方法もある。
関数自作するとか。排他制御ができれば後は自分でいろいろできる。
546:デフォルトの名無しさん
06/06/25 06:33:19
Fiberでも使ってみたら?
NT以降専用だが。
547:デフォルトの名無しさん
06/06/25 07:21:53
以前も微妙に話題になったが、MeteredSectionとか、
AdvancedWindowsのOptExとかで楽しちゃうってのもある。
MeteredSection
URLリンク(msdn.microsoft.com)
OptEx
URLリンク(www.microsoft.com)
548:539
06/06/26 17:26:10
>>545
>>546
>>547
とりあえず、情報どうも。Fiberだと排他・同期というより切替に近いので、同期にはならない気がする。
問題は、CriticalSection系は、同一スレッド上では同期しないので、>>545さんの方法、OptExはちょっと難しい感じ。
つーか、それだとどちらか片方の処理が重いときにバグになっちまう。
ということで、MeterdSectionを使うのが良いのかなぁ。でも、こいつ、CriticalSectionの拡張版みたいなもんみたいだから、
排他は出来るけど、同期は難しそう。
探したら日本語のページがあったので貼り付け。
URLリンク(www.microsoft.com)
それとも、原始的にロックファイル作るか…うーん、100近いスレッドが立ったとき考えるとやりたくないな…。
とりあえず、Mutexあたりを、最初にまとめて作成することで負担を減らすしかなさそうな感じ。
みなさん、ありがとう。
では。
549:デフォルトの名無しさん
06/06/27 21:28:46
>>548
具体的にどんな処理がしたいの?
単一プロセス内ならCriticalSection で何でも作れるよ
(だからこそOSもCriticalSectionしか提供してないわけで)。
あと「同一スレッド上では同期しないので」ってのはどういうことを
意図してるのかちょっとわからない・・・同一スレッドで非同期に
コードが実行されるのは Unix の signal のケースくらいで、
Windows のユーザモードでは起きないんじゃない?
550:デフォルトの名無しさん
06/06/27 22:11:04
>>549
ありゃ、そういう答えが返ってくると言うことは、
もしかして オレのCriticalSectionについて根本的な使い方や理解が間違ってるのかな…。
スレッドひとつしか無ければ、EnterCritical...をいくつ並べても無視されると思ってたんだけど、違うのかな…。
551:デフォルトの名無しさん
06/06/27 22:13:57
スレッドひとつならマルチスレッドじゃないからな
552:デフォルトの名無しさん
06/06/27 22:21:17
そんなにパフォーマンス気にしてるのに
何でロックファイルとかにいくんだ…
553:デフォルトの名無しさん
06/06/27 22:39:45
英語でプログラミング勉強スレ
スレリンク(english板)l50
554:539
06/06/27 22:53:13
>>551
でも、プログラムは大抵、ひとつのスレッドから開始されるんだよ。最初からふたつってわけじゃない。
まあ、だから、>>545 は使えないんだよな。
555:デフォルトの名無しさん
06/06/27 23:12:31
なにか、壮大な勘違いをしている予感
556:デフォルトの名無しさん
06/06/28 01:48:11
まず、「同期する」をどういう意味で使っているのか説明してもらってからだな
557:デフォルトの名無しさん
06/06/28 01:56:37
同期の人と一緒に仕事をする
同期するじゃね?
558:デフォルトの名無しさん
06/06/28 03:31:41
>>539
そういうのは、ふつーにeventだの何だのをでも使ってみて、重くてやってらんなくなってから考えれば良い。
559:539
06/06/28 14:26:16
>>558
なんだそりゃ。そう言う回答は勘弁して欲しいぞ。
560:デフォルトの名無しさん
06/06/28 16:09:22
なんで>>539は質問してる身分でやたらと偉そうなの?
リアルでもそういう質問の仕方しか出来ない人?
561:デフォルトの名無しさん
06/06/28 16:16:15
>>559
「回答」ではないだろう。
「重そう」と思うだけで実際にやってみることもせず
そのくせ「ロックファイル」なんて単語まで飛び出し
結局何がしたいのか、「同期」が何を指しているのかも
説明してくれない君への
アドバイスじゃまいか。
562:デフォルトの名無しさん
06/06/28 16:45:09
おまえら馬鹿なんじゃね?
563:デフォルトの名無しさん
06/06/28 16:54:40
>>560-561
ウザ
564:デフォルトの名無しさん
06/06/28 17:45:49
つーか、時々デッドロックするようなプログラムしか作ってないのかょ、おまえらは。
565:デフォルトの名無しさん
06/06/28 17:46:52
なにか、壮大な勘違いをしている予感
566:デフォルトの名無しさん
06/06/28 18:17:22
なにかが気に喰わなくて暴れ始めたお母ん
567:デフォルトの名無しさん
06/06/28 18:54:40
予感でプログラムが組めるヤツはニュータイプ。
568:デフォルトの名無しさん
06/06/28 21:21:38
まてまて。
>>539 はWindows のカーネルやAPIを設計した人たちや
普段からマルチスレッドAPなんかさんざ書いてる漏れも
見落としている、何か難しい問題に対処しようとしてるんだよ。
それはたぶんロックファイルと関係のあるなにかなんだろう。
漏れには想像もできないが、頭ごなしに否定しないでだまって
観察してあげるべきじゃないか?
569:デフォルトの名無しさん
06/06/28 22:34:06
なにか、壮大な勘違いをしている予感
570:asdlman
06/06/28 22:40:26
>>569
>>555
lead reth !!!
571:asdlman
06/06/28 22:47:52
>>569
>>555
test
572:デフォルトの名無しさん
06/06/28 23:21:13
Vistaから同期IOのAPIがキャンセル可能になるそうですね!
573:デフォルトの名無しさん
06/06/29 13:12:51
それは、非同期IOのことだろ。
574:デフォルトの名無しさん
06/06/29 15:47:52
>>573
同期IOだよ。非同期IOのキャンセルならWindows98でも出来る。
575:デフォルトの名無しさん
06/06/29 15:54:38
それを非同期というのではないのか?
576:デフォルトの名無しさん
06/06/29 16:11:59
>>575
めんどくさいなーもう。ほらよ↓
今まではCerateFile なんか非同期版がなかったからキャンセルできなかっただろ?
URLリンク(www.microsoft.com)
577:デフォルトの名無しさん
06/06/29 16:29:46
>>575
いや、絡んでるつもりは全くなかったんだ、すまん。
つまり、同期I/Oを他のスレッドで実行することによって擬似非同期I/Oの
ような使い方をしたときに、そのAPIの実行をキャンセルすることができる
ようになったということだな。
同期/非同期とはそういうことだったのか。勉強になった。
578:デフォルトの名無しさん
06/06/29 16:30:17
s/>>575/>>576/
579:デフォルトの名無しさん
06/07/01 17:16:56
Linuxのカーネル層でマルチスレッドのような
設計が必要になっているんですが、
Aという関数が終わったらBの関数で
止めていたところが動き出すような設計って
どうやったら良いもんですか?
セマフォを使った排他処理ってデータに
対する排他処理になると思うんだけど、
そういう形で発想の転換をしないと駄目なのかな?
580:デフォルトの名無しさん
06/07/01 19:10:53
待ちたいところでスピンロックでもasm WAITでもなんでもしておけばいいだろう
581:デフォルトの名無しさん
06/07/01 21:33:47
>>579
condは適当に初期化。
A() {
〜;
pthread_cond_signal(&cond);
return;
}
B () {
pthread_cond_wait(&cond);
〜;
return;
}
582:デフォルトの名無しさん
06/07/01 22:07:31
>>581
参考になります。
これってAのreturnの直前でBが動き出す
ということですよね?
Aが終わってからってのはやっぱり難しいのかな。
583:デフォルトの名無しさん
06/07/01 22:20:08
>>582
AA() {
A();
pthread_cond_signal(&cond);
return;
}
584:デフォルトの名無しさん
06/07/01 22:23:13
>>522
昔の奴へのレスで恐縮なんだがWindowsのSleepってそんな精度悪かったっけ?
::timeBeginPeriodとか使っても駄目?
585:デフォルトの名無しさん
06/07/01 22:49:29
他に忙しく仕事をする連中が居なければ、だいたいは大丈夫かもね。
586:デフォルトの名無しさん
06/07/01 23:26:40
>>585
なるほど
まあそれほど信用できないってことか。
587:デフォルトの名無しさん
06/07/02 00:04:19
スレッドの教科書ってどんなのあるの?
アルゴリズム系に強いやつが欲しい
588:デフォルトの名無しさん
06/07/02 00:14:48
OSによって違うな。
pthreadなら>>1->>9辺り見て。
589:デフォルトの名無しさん
06/07/02 00:18:29
Lamport's bakery algorithmとかさこんな古典的なやつから
今の新しいアルゴリズムまで載ってるのないのか.....
590:デフォルトの名無しさん
06/07/02 00:49:51
>>583
AAは別の人のソースなので手を加えられないのです。
でも参考になりますた。
ありがとうございますた。
591:デフォルトの名無しさん
06/07/03 06:53:31
windows には win32 API で色々なイベントを使えるようになってますが、
UNIXではpthreadのイベントを使う以外にないのでしょうか?
592:デフォルトの名無しさん
06/07/03 07:26:25
>>591
例えばこういう奴?
URLリンク(www.monkey.org)
どの UNIX を対象にしているかで答えも変わるけど、大抵はググれば色々出て来る。
593:デフォルトの名無しさん
06/07/05 10:16:26
Winsock、_beginthreadで起動して、
グローバル変数で終了要求するような、
簡単なスレッド書いているのですが、、、。
スレッド内でrecv()のような、
ブロックするような関数を呼びたくなりました。
これを終了するにはどうしたらよいでしょう。
594:デフォルトの名無しさん
06/07/05 10:30:57
ソケットオプションでノンブロッキングにすればいいんでない?
595:デフォルトの名無しさん
06/07/05 11:33:24
594>
ありがとう、そうします。
一般的にはどうでしょ?
ブロッキングするような関数をスレッドで呼んではいけない?
596:デフォルトの名無しさん
06/07/05 11:51:06
ブロッキングするべき状況と、そうでない状況がある。
前者ならブロックさせとけばいいし、そうでなければ非同期APIを使うか、
別個にスレッドを作ればいい。
597:デフォルトの名無しさん
06/07/05 11:59:52
>>593
recv()呼ぶ前にMSG_PEEKしておくなりしとかんとあかんよ。
#つーか、TCP受信処理を途中で終わらせると言う仕様そのものが如何なものかと。
598:デフォルトの名無しさん
06/07/05 13:48:39
>>595
一般的かどうかは知らないけど、普通はselect使うんじゃないかな。
599:593
06/07/05 13:49:40
>>596
「強制終了」以外は普通にブロックして、データが届いたときだけ
処理してくれればいいんだけど。って状況でした。
非同期っていうと、WSAEventSelect, WSAAsyncSelectで、通知を待つって
ことでよいですよね?
>>597
ちょっとわかんないのですが、
MSG_PEEKで、受信データが無いときはどうやって次の受信データを待つのが綺麗?
あと、途中で終わらないとしたら、どうやって終わるのがよいですか?
600:デフォルトの名無しさん
06/07/05 18:14:11
>>599
WSAAsyncSelect(s, hwnd, 0, 0);
shutdown(s, 1);
while (recv(s, buf, buflen, 0) != 0) {}
closesocket(s);
601:デフォルトの名無しさん
06/07/05 19:33:20
>>600
それ先方がデータ送ってくれないとCPU100%のビジーループ。
サーバアプリでは非常によろしくないコーディング。
602:デフォルトの名無しさん
06/07/05 19:50:55
recvって、ブロックするんじゃないの?
603:デフォルトの名無しさん
06/07/05 19:54:13
スマソ。ノンブロッキングのソケットと勘違いしてた。
604:デフォルトの名無しさん
06/07/05 20:35:06
WSAAsyncSelectした時点でノンブロックになる
605:593
06/07/06 17:50:25
任意のタイミングで終了させたいスレッド内では、ブロックする関数は呼ぶな。
socketはデフォルト非同期で。
で理解しました。 ありがとう。
606:デフォルトの名無しさん
06/07/06 19:44:58
>>605
そう理解したんならそれでもいいが・・・
607:デフォルトの名無しさん
06/07/06 22:55:34
ソケットを任意のタイミングで終了させると再起動したときにわややがな。
608:593
06/07/06 23:34:44
>>607
どゆこと?
例えばサーバ的なアプリで、その受信用スレッドを、サーバ的なアプリ
のユーザ都合でブチっとしたくなった場合、、、
受信処理を終了させて、クローズなり何なりをしたい。
そんな場合ですが、再起動とは、ここでサーバ的なアプリを再度起動して
受信を始めようとした場合になにかが起こるってことですか?
609:デフォルトの名無しさん
06/07/07 00:34:15
下の層が受信しているのにアプリが落ちたら、次に起動するときにbindErrorになる。
610:デフォルトの名無しさん
06/07/07 00:43:27
つかさ、断片的な情報を積み重ねて信頼性のないアプリ作るよりさ、
ばしっとWinsock Programmer's FAQとか、Winsock関連書籍を読み
とおして、きちっとしたアプリを作ろうとは思わんのかね。
611:デフォルトの名無しさん
06/07/07 01:07:01
>>609
アドバイスするなら、その前にFAQくらい読んどけよ。
知識足りなさ過ぎ。
612:593
06/07/07 10:11:03
>>610
終了するときは、shutdown(sock, 1) をスレッドの外から呼べ、
そうするとrecvが0返すので、スレッドを抜けろ。
で、理解しました。ありがとう。
613:デフォルトの名無しさん
06/07/07 11:42:52
>>609
100%賛成して同意して応援します。
他人のいう事に惑わされたりマニュアルやFAQを読んだりせず、
先方が送信を続けている間は終了できないプログラムを作り続けてください。
614:デフォルトの名無しさん
06/07/07 11:56:22
>>593
fcntl(s, F_SETFL, O_NONBLOCK); って使えない?
(Windowsだとこれはないのかな?)
>>595
select() 使う場合は他のスレッドが同じソケットから読まないように
作る必要がある。recv() ではあまりないかも知れないが、サーバ用に
bind() した一つのソケットに対して複数のスレッドから accept()
する場合にselect()使うとハマる(2つ以上のスレッドがselect()を通過
した場合に一つのスレッド以外がブロックする)。防止するには上に
書いたような fcntl() で O_NONBLOCK セットして accept() で止まら
ないようにする。
>>609
最初に setsockopt() で SO_REUSEADDR をセットしとけばいいんじゃないか?
615:デフォルトの名無しさん
06/07/08 02:08:08
volatile最強
616:デフォルトの名無しさん
06/07/08 10:36:46
メモリモデル勉強しる
617:593
06/07/09 20:09:28
最後にもひとつ。
一般的に、マルチスレッドで使われることを前提にした、受信待ちのような
ブロックする関数を提供しようとした場合は、それと一緒に受信を中断
して安全に返るための、他のスレッドから呼ばれる関数(socketの場合のshutdown)
を提供すればよい。
ですか?
他に綺麗な方法あったら教えてください。
618:デフォルトの名無しさん
06/07/09 20:26:23
一般的には、recvでブロックさせるのではなく
select系でブロックさせて
selectをブレークさせる方法を使うだろ。
つまり「外部から解除できないブロッキング関数ではブロックさせない」と。
619:デフォルトの名無しさん
06/07/09 20:28:20
selectってキャンセル可能だっけ
620:デフォルトの名無しさん
06/07/09 20:36:02
selectで同時にパイプを待ったり
WSAEventSelectで同時にEventを待ったり
というやり方のこと。
621:デフォルトの名無しさん
06/07/09 20:39:08
selectでtimeout設定すればいいと思うけど
それとももっと高度な制御がしたいの?
622:デフォルトの名無しさん
06/07/10 00:20:40
>>621
timeout 値の設定がめんどい。
仕方なく >>620 の WSAEventSelect 使ってるけど、
これはこれでめんどい。
623:デフォルトの名無しさん
06/07/10 03:56:03
pselect
624:593
06/07/10 10:22:26
>> 618
なるほど。
Linuxでは、読み込むものは全部ファイル(ファイルデスクリプタ)で、
中断など例外的な処理は全部シグナル。と思ってpselectで綺麗にい
けていたんだけれど、
Winにいったら、いろんな方法がありそうだけれど、どれもいまいちに見えて。。
WSAEventSelectは、socket以外にも汎用的に使ったりする?
625:デフォルトの名無しさん
06/07/10 11:23:05
>>617
スレッド(タスク)開始、実行状況(実行結果)の取得、完了待ち、中断のための関数を
それぞれ提供するのが一般的だと思います。
あと場合によっては先方のスレッドでコールバックされる実行状況の通知のための
コールバック関数なんかも設定できると UI 作るときには便利(プログレスバーとか)。
>>624
個人的な意見だけど、Windows では(ソケットなら WSAEventSelect等も使って)
(Msg)WaitForMultipleObjectExで待機、何かあれば処理するというのがマルチ
スレッドの場合の定石だと思います。
ソケットだけ相手にするなら単にclosesocketしてしまうというのもアリだと思うけど。
>WSAEventSelectは、socket以外にも汎用的に使ったりする?
ソケットを相手にしないのにWSA〜を使う局面は無いと思う。
626:デフォルトの名無しさん
06/07/11 00:19:53
>>624
Winでも sock をファイルとして扱えるよ。
627:デフォルトの名無しさん
06/07/18 19:03:35
Linux (Kernel 2.6.17) で、pthread_create() で複数スレッドを作り、
そのスレッド全てが同一の bind(), listen() されたソケットに対して
accept() を行うプログラムを作ったのですが(O_NONBLOCKなソケット
ですが)、クライアントプログラムから複数 connect() すると、
accept() が同じ値のファイルディスクリプタを返して来ます。処理を
単純に書くとこんな感じです。
for(;;) {
int cs = accept(...); // ここで cs が別スレッドと同じ値になる。
if (cs == -1) {
if (errno == EAGAIN) {
usleep(50000);
continue;
} else {
break; // error
}
} else {
proc(cs); // cs を使った処理。
close(cs);
}
}
accept() の前後に pthread_mutex_lock(), pthread_mutex_unlock() で
ロック、アンロックをしても同じでした。
これって Linux のバグなんでしょうか?
これを回避するにはやはり accept() をするスレッドを一つにしないと
駄目ですか?
628:デフォルトの名無しさん
06/07/18 19:04:29
すいません。質問なのに age 忘れました。age ておきます。よろしくお願いします。
629:デフォルトの名無しさん
06/07/18 19:32:26
わざわざageんなよ
630:デフォルトの名無しさん
06/07/18 22:10:43
コンパイル・リンク時のコマンドライン見せろ
631:デフォルトの名無しさん
06/07/19 01:14:42
>>627
> accept() の前後に pthread_mutex_lock(), pthread_mutex_unlock() で
> ロック、アンロックをしても同じでした。
この時点でプログラミングの問題じゃないことに気付け。
632:デフォルトの名無しさん
06/07/19 01:56:34
>>627
>これって Linux のバグなんでしょうか?
つかその前に、どこで習ったんだそんな阿呆な手順。
633:デフォルトの名無しさん
06/07/19 02:07:07
acceptしてからthreadおこせ
634:デフォルトの名無しさん
06/07/19 03:17:38
>>627
よく知らんけど、下記のApache のディレクティブの存在なんかを見ると
accept は直列化 (同時に1つのプロセス・スレッドからのみ呼ぶ)するのが
普通なんじゃないかと思うが・・・
URLリンク(httpd.apache.org)
635:デフォルトの名無しさん
06/07/19 09:14:39
>>630
gcc hoge.c -lpthread
>>631
どういう意味ですか? 同時に複数のスレッドが accept() しないように
しただけですが?
>>632
これは阿呆な手段ですか? 何故? その辺詳しく教えてもらえますか?
どこで習ったかは記憶にありません。ただ昔 Java で似たようなものを
作った時はちゃんと動いたと思いました。
>>633
それはつまり accept() は親(?)スレッド一つでやるということですね。
636:デフォルトの名無しさん
06/07/19 10:02:36
accept()を排他しても、
そのプログラムがまともにうごかん事に疑問はないのか?
他の部分がおかしいだけではないのか?
637:デフォルトの名無しさん
06/07/19 11:14:26
>>636
そこが疑問ですよ。なんで既に取得してオープンされているファイル
ディスクリプタと同じ値のファイルディスクリプタが返って来るのか
という点がね。
気になるのは Linux の場合スレッドは clone() システムコールで作った
特殊な別プロセスであるということです。別プロセスだから複数スレッドで
accept() した時に同一ファイルディスクリプタが取れてしまうんじゃない
かな、とは思ったんですが、確証がなかったので質問したんです。
(Linux板の方で質問した方がよかったかな? でも他のOSではこういうのは
できないのかも少し気になる)。
で、その後、accept() を一つのスレッドでだけやって、他のスレッドは
待機させておいて、accept() 成功後にファイルディスクリプタを待機
スレッドに渡すように作り替えたらちゃんと動くプログラムは作れました。
理由はどうあれこういう風にするか、あるいは accept() 成功後にスレッド
作るように書かないと駄目なようですね。
638:デフォルトの名無しさん
06/07/19 11:25:03
>>627
> int cs = accept(...); // ここで cs が別スレッドと同じ値になる。
本当にこれと同じ書き方だったんだな?
csが大域変数だったってオチがありそうだ。
639:デフォルトの名無しさん
06/07/19 11:30:07
>>638
いいえ。大域ではありません。autoです。
640:デフォルトの名無しさん
06/07/19 12:26:13
生半可な学習しないで、ファイルディスクリプタについてちゃんと勉強したら?
641:デフォルトの名無しさん
06/07/19 13:35:02
>>637
>スレッドは clone() システムコールで作った
ちょwwwwおまwwwww帰れwwwww
642:デフォルトの名無しさん
06/07/19 13:56:58
げ、pthread_createで作ってんじゃないのか・・・!!!
643:デフォルトの名無しさん
06/07/19 14:24:08
>>640
生半可? どの辺がですか?
>>641-642
pthread_create() で作ってますよ。clone() は内部動作の話です。
644:デフォルトの名無しさん
06/07/19 14:29:47
こいつむかつく〜☆
645:デフォルトの名無しさん
06/07/19 14:36:37
>>643
コンパイルオプションとかはどうだろう・・・ -pthread とか付け忘れありませんか?
646:デフォルトの名無しさん
06/07/19 14:42:25
>>645
>>635
647:デフォルトの名無しさん
06/07/19 14:48:29
>>643
続きは以下で。
ネットワークプログラミング相談室 Port17
スレリンク(tech板)
648:デフォルトの名無しさん
06/07/19 14:59:44
>>646
-lpthread だけ指定して -pthread を指定していないのではないか、といっているのだが。
649:デフォルトの名無しさん
06/07/19 15:38:16
>>648
-pthread は指定していませんでしたが、man ページや info 見ると
Linux では関係ないようですよ。
念のため指定してテストしてみましたが、同じ動作になりました。
650:デフォルトの名無しさん
06/07/19 15:45:45
いい加減、誰かファイルディスクリプタの説明してやれよw
651:デフォルトの名無しさん
06/07/19 15:47:52
>>647
そっちの方がいいですか?
スレッドとネットワークと Linux が混ざった疑問なのでどこがいいのか迷ったんですが。
652:デフォルトの名無しさん
06/07/19 16:00:17
>>640>>650
pthread_createで作ったスレッド間では、ファイルディスクプリタは共有されます。
すなわち、同じ数値=同じソケット端点。
何がいいたのか知らんがこれでいいか?
653:デフォルトの名無しさん
06/07/19 16:06:07
>>651
どっちがいいのかはわからんけど、このスレじゃ解決できなさそうじゃん。
654:デフォルトの名無しさん
06/07/19 16:20:45
Linuxのスレッドが特別なプロセスだったのは、2.4より前の話だよ。
# 例えばpidやsignalの扱いなど。
>>627は、2.6.17だから関係ない。
655:デフォルトの名無しさん
06/07/19 16:21:59
>>637
> accept() 成功後にスレッド作るように書かないと駄目なようですね。
そんなことはない。
656:デフォルトの名無しさん
06/07/19 16:22:13
>>652
それは知ってますよ。だからこそOSがおかしいんじゃないかと思ったんですから。
元々の質問を要約すると、まだ close() されていないファイル
ディスクリプタの値を accept() が返して来るのは何故かです。
一つのスレッドが accept() で 5 を受け取ったとして、それが
close() される前にもう一つのスレッドが行った accept() が
受け取ったファイルディスクリプタの値も 5 だったということです。
これ、accept() を一つのスレッドでしかやらなければ当然 6 などの
違う値が返って来ます。
>>653
今のところそんな感じしますね。
657:デフォルトの名無しさん
06/07/19 16:24:17
>>654-655
そうですか…。じゃあ何でだろう?
658:デフォルトの名無しさん
06/07/19 16:26:01
こっちの方がいいかも。
UNIX板 pthread地獄
スレリンク(unix板)
読んでる人はこの板と重複がかなりあるかもだけど。
659:デフォルトの名無しさん
06/07/19 16:34:42
>>658
そこはたしかに近いこと話し合われてたスレですね。
じゃあそこに移転します。
こちらのスレのみなさまありがとうございました。
660:636
06/07/19 23:13:46
な?
661:デフォルトの名無しさん
06/07/20 01:05:07
うはwww
ボッコボコwww
しかも自分のミスwww
662:デフォルトの名無しさん
06/07/20 12:50:33
>>640>>650
お前等も黙ってないで「ファイルディスクリプタの説明」してくれよw
663:デフォルトの名無しさん
06/07/20 13:12:28
File Disk Riptor 略して FDR である
664:デフォルトの名無しさん
06/07/20 14:35:46
なんか車の駆動方式っぽい略称だな。
665:627
06/07/20 16:04:12
申し訳ありません。私がバグっておりました。
accept() 後の処理を別関数でやっていたのですが、その中で
fdopen() を使って accept() で受け取ったファイルディスク
リプタから FILE * を作って fgets() や fprintf() を使って
いました。それでその関数から返る直前に fclose() をして
いますが、そうすると当然元となったソケットも close()
されます。
このことをすっかり忘れていたためこの別関数から戻って
来ても accept() で取ったファイルディスクリプタは
オープンしたままだと思い込み、別関数から帰って来てから
close() までの間に別スレッドで accept() した時の
ファイルディスクリプタと同じ値になることがあったため、
今回の疑問に繋がっていました。
ということでお騒がせしました。
Linux でも複数スレッドからの accept() は問題無くできます。
666:デフォルトの名無しさん
06/07/20 16:25:36
まあせいぜい地雷原を走り抜けてくれ
667:デフォルトの名無しさん
06/07/21 20:41:07
Javaだと複数のスレッドがacceptでブロックしてても動いたと思う。
URLリンク(www.amazon.co.jp) の例の1つにあったと思う。
個人的にはとても気持ち悪いし、他の人にコードを見せると頭を抱えるし、
特にメリットも思いつかないので、
自分はJavaでもacceptは1つのスレッドで実行してる。
668:デフォルトの名無しさん
06/07/21 20:54:45
この人の場合、さらに non-blocking にしてあって、ブロックしないでポーリングしてるらしいよw
669:デフォルトの名無しさん
06/07/21 21:16:04
>>667
> 特にメリットも思いつかないので、
はあ
670:デフォルトの名無しさん
06/07/22 00:47:01
試したことはないが、
複数のThreadがServerSocket#accept()でブロックされてるとき、
ブロック解除で起こされるThreadはたぶん唯1つなのがメリットじゃないかな。
単一のThreadがaccept()待ち→ブロック解除→Queueに放り込んでWorker Threadを
たたき起こすパターンだと、Queueを監視する複数のWorker Threadがいったん
たたき起こされるから。
671:デフォルトの名無しさん
06/07/23 09:39:44
pThreadsについての書籍を探してるのですが、以下のもの読まれてる方
感想はどうですか?良書ですかね?
URLリンク(www.amazon.com)
URLリンク(www.amazon.com)
672:デフォルトの名無しさん
06/07/26 17:24:59
お世話になります。
識者のご意見ください。
以下のような状況です。(環境はVC6のWin32API)
メインスレッドAと、待機スレッドBがあります。
BはSuspend状態です。
あるタイミングでAがBに仕事を投げてResumeしました。
Bは仕事が終わったら自分でSuspend状態になりたいのです。
Bは自分のHANDLEをSuspendThreadしちゃってもいいものでしょうか?
AがBのフラグを見てSuspendしてやるというのはちょっと効率が
悪い気がします。
以前はBを待機スレッドではなく、その都度生成して自殺させていました。
が、この方法だとデバッグウインドウに生成と消滅のメッセージが
出まくるのと、やはりCreateThreadの負荷が気になります。
こういう場合の常套手段など、ご教示ください。
待機スレッドがCPUを食わないようにするために、Suspend状態に
しておく、という風に考えるのは変でしょうか?
よろしくお願い致します。
673:デフォルトの名無しさん
06/07/26 17:36:12
>>672
ただのワーカースレッドでええんちゃうかと
マイクロスレッドのようなものを考えてるなら、ファイバでも使えばいい
674:デフォルトの名無しさん
06/07/26 17:36:23
>>672
自分をSuspendするのは悪くない。というか、それが唯一のSuspendThreadの正しい使い方。
基本的には自スレッド以外をSuspendThreadしてはいけない。
ただし、普通はautoresetのイベントハンドルを用意して WaitForSingleObject で待たせておき、
SetEventで起こす。これはAがBをResumeThreadで起こすのが難しいから。
具体的には、SuspendThread/ResumeThreadで待機を実現しようとすると、
ResumeThreadするときに、ちょうどBが何かの仕事を終えてSuspendThreadする
直前だったりするとResumeしても即Suspendしてしまうので、これを避けるために
クリティカルセクションとフラグが1つ余分に必要になってしまって効率も悪いし
コードもムダに複雑になる。
675:デフォルトの名無しさん
06/07/26 18:07:51
みなさん、レス有難うございます。
>>674
>autoresetのイベントハンドルを用意して WaitForSingleObject で待たせておき
こんな感じでしょうか?
スレッドB{
while(1){
WaitForSingleObject(XXX);
何か仕事(B*)
}
}
スレッドA{
while(1){
何か仕事の準備(実行中のB*の部分を触らない配慮はしている)
SetEvent(XXX);
}
}
>直前だったりするとResumeしても即Suspendしてしまうので
なんとなく現象の心当たりがあります。
676:デフォルトの名無しさん
06/07/26 18:21:59
>>675
Bが待機するところはそんな感じです。
あとまぁよく使うパターンとしては
(1) AがBの仕事の完了を待たずににいくつもの仕事を投げておけるようにQueueに仕事を登録して SetEvent する。Bは起こされたら、Queueにある仕事を全て実行してからWaitする。
(2a) 「仕事終わったよイベント(初期値はON)」ってのも用意して、Bが仕事を終えたらSetEventする。Aは仕事終わったよイベントを待ってからBを起こす
(2b) CreateSemaphoreでMaxCount 1 のセマフォを作って、AはWait,→仕事の準備→ReleaseSemaphore、
BはWait→仕事を取り出して実行→ReleaseSemaphore とする。
仕事が終わったかどうか知りたくなることも多いので、漏れは2aのパターンを多用します。
殆ど等価な 2b でもいいのかも知れない。
677:デフォルトの名無しさん
06/07/26 19:36:15
>>676
Suspend,Resumeでやってたところを待機オブジェクトにしました。
すっきりした感じがします。
>(2a)
なんとなくこれっぽいことはやっていました。
どうもありがとうございました。
678:デフォルトの名無しさん
06/07/26 21:47:50
lock-freeについて調べている内に
CAS(CompareAndSwap)という概念にたどりついたのですが
ちょっと教えてください。
/* 関数内の処理はatomicに処理されると仮定*/
bool CAS(void*addr,int expval,int newval){
if(*addr == expval){
*addr = newval;return true;
}
return false;
}
*addr=0;
while(!CAS(addr,0,*addr + 1)){
//※ここでコンテキストスイッチ
}
//アトミックにインクリメント成功?
例えば※の部分で別スレッドがaddrの値を5に変更→0に変更
という動きをした場合、*addr == expvalが成り立ってしまい、
インクリメント成功とみなされてしまう気がするのですが・・・
何か回避策等あるんでしょうか。それとも俺の理解不足なんでしょうか
679:デフォルトの名無しさん
06/07/26 23:07:03
>>678
「アトミックなインクリメント」というものを理解していないような気がする。
過去に5だったかどうかはさておき、他のスレッドで0にされたものが1に
なるのだから成功している。
というか5で放置されてて6になってもやはり成功。
*addr=0;
IntgerlockedIncrement(&addr); ←APIによるアトミックなインクリメント
などとしたところで、この2行の間に*addrが5になって0になることもある。
「アトミックなインクリメントに失敗」というのは、2つのスレッドで1度ずつ
アトミックなインクリメント操作が行われたにも関わらず2増えないことを言う。
これは正しい実装では決して起きない。
680:デフォルトの名無しさん
06/07/26 23:07:59
追記:
CASによるアトミックなインクリメントの実装は、正しくは↓
while (!CAS(addr, *addr, *addr+1) {}
681:678
06/07/27 21:49:17
>>679
>>680
ありがとうございます。
addr=&val;
while(!CAS(addr,*addr,*addr + 1)){
//※ここでコンテキストスイッチ
}
でも問題が起きそうな気がするんですが・・
とここまで書いて
URLリンク(www-06.ibm.com)
に似たような事が書いてありました
ABA問題というらしいですが
もうちょっと勉強してでなおしてきます・・
682:デフォルトの名無しさん
06/07/27 23:41:31
とりあえず、CAS(xxx, *addr, *addr+1)
なんて書いている奴は氏んでいいよ。
atomic_inc(int *addr)は
do {
int var = *addr;
} while (!CAS(addr, var, var+1));
にしないと。
まあ、複数のスレッドで勝手に値をセットするような変数を
特定の個所だけatomic_incを使っても意味は無いがね。
683:デフォルトの名無しさん
06/07/28 00:03:12
終わった話題についてなにを得意げに語ってるんだろう…。
684:デフォルトの名無しさん
06/07/28 00:20:12
>>682
それC言語的にやばいやん。
685:デフォルトの名無しさん
06/07/28 00:31:47
つまり、「ほんと、バカしかいないんだね」ってことか。
686:デフォルトの名無しさん
06/07/28 00:43:19
やーい
ばかー
687:デフォルトの名無しさん
06/07/28 07:26:02
volatileですべて解決!
688:デフォルトの名無しさん
06/07/28 12:24:47
>>682
バカは喪前のほうだと思うが・・・
なぜ自分がバカなのかが知りたかったら、
元のコードの問題点を書いてご覧よ。
書いてるうちにわかるだろ。
689:デフォルトの名無しさん
06/07/28 17:51:17
元のコードの問題点は関係なくて
*addrを2度読み出して同じ値が取れると思っているのがバカだって事だろ。
690:デフォルトの名無しさん
06/07/28 17:53:25
しかも、この手のバグは非常に表面化しにくく
再現性が低い(デバッグが難しい)。
691:デフォルトの名無しさん
06/07/29 15:08:13
volatileですべて解決するという忠告がみえないのか?
次ページ最新レス表示スレッドの検索類似スレ一覧話題のニュースおまかせリスト▼オプションを表示暇つぶし2ch
5360日前に更新/278 KB
担当:undef