C言語なら俺に聞け 142 [無断転載禁止]©2ch.net
■ このスレッドは過去ログ倉庫に格納されています
>>356 たぶん原因が分かりました。 現在は標準関数をやめてWINAPIのCreateThreadを使ってますが標準関数と原因は同じです。 CreateThreadでThread1,Thread2を作成して監視してるとあるタイミングでThread2が消失してたのですが どうやらThread1で変更があった他のグローバル変数の値がThread2で上手く読めずに終了判定されて消えてたようです。 ようするに他のグローバル変数の書き換えが原因でした。 ロック処理をしたりしたのですが上手く行かずグローバル変数すべてにvolatileをつけると上手く動いてるようです。 でもこの回避作で良いんでしょうか?CriticalSectionを使ったりして処理してもうまく他スレッドでグローバル変数が 参照出来なかったのでvolatileをつけてみたら完璧に動作するようにはなったんですが…… 排他制御の問題ではなく 最適化の作業で、変数を不変値と判断し、変数参照を追い出した翻訳をしたパターン int a; // グローバル変数 void foo() { a = 1; while (a) { /* */ } } a は文意から不変の定数として扱えるので while (1) { /* */ } に翻訳しちゃえるわけさ >>359 その変数達の最適化を無効にしたということですね。 ということはこの回避方法で大丈夫だということですか? volatile つけるのは1つの手ではあるが 100% 回避してくれるかは、コンパイラ次第 スレッドで共有するフラグ関係はOS側が用意してる機構を使うほうが良いんだけどねー >>361 一旦コンパイルして普通に動作してるなら大丈夫ですか? 他の環境でいきなり動かなくなるとかもありますか? 何の変数を使ってるか知らんが、排他取ってねーんだから書き換え途中の変数を読み取ってバグることもあるだろ 最近のVCはvolatileだけでマルチプロセッサ対応のメモリバリアになるらしいから、古いVCでビルドしてもアウトだな まあ滅多に起らないからローカルでそれっぽく動けばいいのなら、十分じゃねーの >>358 いや、359ではなくて、363の言うとおり、 > ロック処理をしたりしたのですが上手く行かずグローバル変数すべてにvolatileをつけると上手く動いてるようです。 これが問題だと思うぞ。つか、ロックしろよ。 volatileの意味分かってるか? それ多分他スレッドから書き換えられたときに自スレッドのその値が更新されておらず、バグるのだと思うぞ。 ただしこの場合は、 > 最近のVCはvolatileだけでマルチプロセッサ対応のメモリバリアになるらしい (>>363 ) ←コマ? が正しいのならvolatileでも確かに治りそうな気はするが。で、これってマジなん? つかね、マルチスレッドでグローバル変数を使いまくること自体がだね、、、 >>363 調べてみたんだが、これか? > Microsoft 固有の仕様 → /volatile:ms の項目 > ・volatile オブジェクトへの書き込み (volatile 書き込み) は、解放セマンティクスを持っています。 > つまり、命令シーケンスで volatile オブジェクトへの書き込み前に発生するグローバル オブジェクトまたは静的オブジェクトへの参照は、 > コンパイルされたバイナリでの volatile 書き込みの前に発生します。 > ・volatile オブジェクトの読み取り (volatile 読み取り) は、取得セマンティクスを持っています。 > つまり、命令シーケンスで volatile メモリの読み取り後に発生するグローバル オブジェクトまたは静的オブジェクトへの参照は、 > コンパイルされたバイナリでの volatile 読み取りの後に発生します。 https://msdn.microsoft.com/ja-jp/library/12a04hfd.aspx これならロックではなくフェンスだね。volatileでは直らないはず。 >>365 自分も見かけただけでちゃんと調べたわけじゃないけど、それだね フェンス+ロックが必要ってのは当然として、最近のVCならフェンスのほうが保証してくれてるってだけ >>361-365 CriticalSectionつかってグローバル変数の書き込み時にはロックをするのも試してみたんですが やっぱりthread2が途中で死んで(内部のループで終了判定になってしまって)、ダメだったんですよね。 それがグローバルにvolatileをつけると普通に動作するようになってるんです…… こういう事ですか? CriticalSectionによるロックもやる必要がある。理由はロックしてない時固有の問題もあるから。 今回の不具合はコンパイラの最適化が原因だったのでvolatileで回避できただけ。 ロック処理もvolatileも両方やる必要がある。ということですか? 書き込み時だけロックして 読み込み側のほうはロックせずに読み込んでる 片手落ちのように受けとれる文章だけど・・・ 排他制御をちゃんとやってたうえでの話? volatileにしてロックして読み書きするのが普通では? >>369-370 volatileにして読み書きロックして厳密にやります! >>368 × > 今回の不具合はコンパイラの最適化が原因だったのでvolatileで回避できただけ。 ○ コンパイラの不具合ではなく、君のコードの問題で、 volatileで動いているように見えるのはバグに当たる確率が低くなっただけで、直ってはいない。 ただ、366の通り、全部ロック+フェンスすれば直りそうな気はする。 つか、そもそもフェンスが大量に要るような使い方でマルチスレッドするのが問題なんだが。 posixだとロックAPIにメモリ同期の効果があるから、普通はvolatileいらんけどな CriticalSectionも共有リソースへのアクセス制御用の関数なんだから、同じはずだと思うけども >>372 コードの不具合って意味だろ? やっぱり最適化が問題だったんじゃないか? >>374 > どうやらThread1で変更があった他のグローバル変数の値がThread2で上手く読めずに終了判定されて消えてたようです。(>>358 ) この場合はvolatileをつけないと話にならない。(ただし本来の使い方ではない) コンパイラのバグではなく、多分デバッグビルド時には毎回読み出しだっただけ。(最適化なし、なおフェンスもなし) データ同期する気がなければフェンスは本来は要らない。 ロックはしないと書きかけの値を掴んだりしてバグる。 ここら辺分かってなくてグローバルで同期ってのはマルチスレッド初心者なのだろうけど、 バグったらsync入れまくってデバッグはセオリーではあるし、まあがんばれ、と。 ただ、おそらく同期の仕方を根本的に間違ってるから、知ってる人に見てもらったほうがいいとは思うが、 コード内のグローバルは常に「最新」を期待しているのであれば、ロック+volatile+フェンスで動くはずでもある。 ロック中の読み出しが最適化で消えるなら、それはロックとコンパイラが腐ってる >>372 > ○ コンパイラの不具合ではなく 誰もコンパイラの不具合なんて言ってない 偉そうに頓珍漢な講釈たれる前に日本語勉強し直してこい >>373 メモリー同期と最適化は別の話 Posix は知らんけど CreateSection にはロック対象を知るすべはない はあ、そうですか 共有リソースの排他アクセスのためのAPIなのに、ロック対象がわからないからロック無視して最適化しますと それが真実ならWinAPIとVCが腐ってるんじゃないですか メモリバリアを跨いでオブジェクトへのアクセス命令を最適化するとか そんな頓珍漢なことを言う腐った奴がいるとは思わなかったわ volatileを何だと思ってんだか CreateSection の使い方ぐらい調べてから書けばいいのに... まあ他人事だしどうでもいいけど w CriticalSectionの話のつもりだったんだけど、CreateSectionに変わったの? それならごめんだわ、>>378 で唐突に出てきて誤字だとばかり さすがにそんなアホな話題転換があるとは思わなかった >>385 ああすまん、予測変換で間違えてたわ で、CriticalSection について調べたのかな? >The following synchronization functions use the appropriate barriers to ensure memory ordering: >Functions that enter or leave critical sections >Functions that signal synchronization objects >Wait functions >Interlocked functions https://msdn.microsoft.com/en-us/library/windows/desktop/ms686355 (v=vs.85).aspx 調べたけどCriticalSectionだけで十分そうだよ Internallockedとかのほうが手軽そうだけどね https://teratail.com/questions/39986 これの投稿 2016/07/04 10:48の例にあるように 最適化で変数の効果が消えてしまう場合でも volatileじゃなく変数書き込みや参照の部分に CriticalSectionでロックしておけば最適化で消える事はないということ? Cのコンパイラはクリティカルセクションなんて知らない。単なる構造体かなんかのtypedefとして実直にコンパイルするだけ。 スレッド関連の関数についても単に関数の一種として扱うだけ。 従ってスレッドやクリティカルセクションを扱っているかどうかが最適化の結果に影響することはない。 一方volatile指定はコンパイラが認識するキーワードで最適化に影響する。 ということは今回の事例だと排他制御しても不具合は解消できなかったという事かな? volatile付けて最適化を無効にすることが正しい解決策だったということになる? >>387 だからそれはメモリーバリアの話で最適化の話とは別だよ 繰り返しになるけど CriticalSection は排他制御するリソースを知ることはできないしコンパイラも CriticalSection のことを知らない >>390 どう言うことをやりたいのかよく見てないけど排他制御と最適化の話は別だから必要なら両方やらないとダメ >>389 それ、スレッドまわりが規格化されてなかった C99 までの話だよね? こんな感じ。 http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf 5.1.2.4/5 "The library defines a number of atomic operations (7.17) and operations on mutexes (7.26.4) that are specially identified as synchronization operations. These operations play a special role in making assignments in one thread visible to another. ..." >>394 おおごめん、規格自体には詳しくない しかし… pthread関連の標準関数や定義を仮にコンパイラが特別扱いしたとすると、標準関数をラップしたり自作ライブラリで代替した場合に最適化の結果が変わるのかな? そんな風にコンパイラ作るものなんだろうか。 posix-thread 関係については C++11 で追加されたが, あくまでも言語処理系の中で定義された識別子(関数とかクラスとか)を使わないと,コンパイラには分かりえないよ. CreateThread() とか CriticalSection とかの W32API の用語を,無指定でコンパイラが理解できるわけがない. >>390 コンパイラは基本的に問答無用で最適化するけど、それだとマルチスレッドではプログラマの意図しない最適化が起こってしまう だから、メモリバリアを挟んだアクセス命令を入れ替えないことにしている 言い換えると、メモリバリア前に読み込んだ値をメモリバリア後も使うような最適化はしない よって、バリア+ロックで十分 共有リソースは全部volatile付けるなんてしてたら、kernelのソースとかvolatileまみれになるでしょ 当然、ローカル変数とか他から変更されないことが明らかなオブジェクトであれば最適化が起こる可能性は否定しない グローバル変数の場合は、最適化されない やってみればわかるけど、CriticalSecltionの有無でアセンブル結果が違う CriticalSectionに限らず未知の関数が呼ばれたのであれば、その中でグローバル変数やらが変更される可能性を考慮しなければならない 他にも、ある関数の引数にローカル変数のアドレスを渡すと、以降の別関数内でそのアドレス経由でローカル変数を変更される可能性がある よって、場合によってはローカル変数であっても関数呼び出しを跨いで最適化できなくなることがある わからないからこそ、最適化してはならないんだよ >>390 ならない。君のソースコードだけの問題だ。同期機構の使い方が悪いだけ。 スレッドなんてここ20年間バリバリに使われてる。 VC等の小慣れているコンパイラなら、最適化ありで全く問題なく動く。 (マルチスレッド向けのデバッグは完了している) Debugでは動くがReleaseでは動かないケースに遭遇した初心者は、君のように 「最適化の問題であり今回はDebugを使うこと」を正当化したがるが、これは間違いだ。 ただしDebugビルドでもバイナリは出来るから、問題ないのならそれを使う手もある。 ちゃんと排他処理が出来ていないことに気付かず、デバッグオプション付きとか 遅いマシンとかで正常動作してしまい、何年後かに速いマシンにリプレイスしたら 丁度良くバグが顕在化するようなタイミングになってしまった、 なんてことが以前本当に起きた。 サーバ用とかでソフトが長期間使われる場合は要注意だ。 歯痛制御のバグ 珍しくない 発見しづらく、再現性も乏しく、解析に手間がかかりがち レベルの低い開発者が混ざると大変 マルチスレッドは、たまたま動いているだけで、 ちょっとしたタイミングで、バグる 初心者は、たいてい、コンパイラのバグのせいにする。 それか、リリースビルドの最適化がおかしいと言う プロは、Elixir などの関数型を使う いや、ちゃんとロックすりゃなんとかなるってw もちろん最初から考慮されてる言語は何も考える必要ないからうまく行って当然。 初心者です。エラーでまくりでコンパイルすら出来ません。アドバイスお願いします。 ■やりたいこと。 マウス入力にrawinputを用いるアプリのWndProcをフックし、rawinput(WM_INPUT)を強制オフにし、DirectInputを使用するように指定。 ↓ソース // FuckRawInput.c #include <windows.h> LRESULT CALLBACK WinProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { switch(Msg) { case WM_CREATE: { RAWINPUTDEVICE Rid[1]; Rid[0].usUsagePage = 0x01; Rid[0].usUsage = 0x02; Rid[0].dwFlags = RIDEV_NOLEGACY; Rid[0].hwndTarget = hWnd; RegisterRawInputDevices(Rid, 1, sizeof(Rid[0])) == FALSE) ; break; } ----- また、この方法が最適なのかも不明です。ほかの方法等もあったら教えてください >>414 そのままの意味でC言語初心者ですけど? rawinputを消し去りたいがためにプログラム未経験ながらC言語入門書を読み、 Visual studio2013でハローワールドのコンパイルに成功した所です。 他スレで聞いたら、 DLLインジェクションでAPI関数を置き換えといわれたんですが、 まぁ、それと同じような事をしようとして書いたソースが>>412 目的達成するためにどこが悪いのか教えてくださいよ どうやってWndProcをフックするつもりなのか知らんが、初心者のやることじゃない >>412 とりあえず文法エラーのないソースを出せよ >>415 windowsプログラムでFuckファックは初心者がやるものではない 何故かと言うとFuckファックは非常に拙いからで、これはご存じの通りにFuckファックつまりはセクロスとかセックスを意味する FuckRawInputなどと書かれると非常に面食らうので、動作には全く関係ないがFuckファックの箇所をHookフックとする Fookという間違いならまだしもFuckが出てくるのは途轍もなく拙い windowsプログラムは入門書を一通り終えた後にようやく手を出せる程度に面倒くさい C++の標準でWindowsのCOMみたいなのはできるの? モジュールがあれば動的リンクできて、向こう側の関数の形式とエントリポイントがわかるような。 >>418-422 WndProcフックでは目的の動作できなくて、APIフックしか手がないと分かりましたので、 MSのdetourとかいうの使ったAPIフックに変更されました。改めて、見てください。 @やりたいこと Rawinputマウス関数が呼び出させられる代わりDirectInputマウス関数を呼び出し、強制的にマウス操作をDirectInputにする ソース↓ // APIhook_RawInput.c #include <windows.h> #include "detours.h" hoge target = NULL; // ターゲットポインタ hoge tp = NULL; //トランポリンポインタ // 関数生成 BOOL WINAPI Direct_Input(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { return tp(hWnd, Msg, wParam, lParam); } // user32インスタンス HINSTANCE xxxxx; case WM_CREATE: //user32.DLLをロード xxxxx = LoadLibrary("user32.dll"); //Raw Inputポインタ取得 target = (hoge) GetProcAddress(xxxxx, "WM_INPUT"); // WM_INPUTフック、"WM_INPUT"→"Direct_Input"置き換え tp = (hoge) DetourFunction((PBYTE) target, (PBYTE) Direct_Input); break; // 終了時の処理 case WM_DESTROY: // フック削除 DetourRemove((PBYTE) tp, (PBYTE) Direct_Input); >>426 で、お前が聞きたいのは「C言語」に関するどんな質問なんだ? 会話にすらなってないな。 こういうのホントにいるんだよなあ。、。 >>415 > どこが悪いのか教えてくださいよ 頭と態度。 APIスレも見たけど、君には無理。 これ無理だろうWindowsプログラムの言語を理解してないと直せないっしょ 勘所がすぐつかめる人ならそうでもない 知らんOS知らん言語知らん環境でもデバッグできちゃうんだぜ あいつら宇宙人だろ >16 :デフォルトの名無しさん:2006/03/10(金) 22:15:19 >>>10 >> こんな変数名があちこちに散らばっているのがPerl > >大学の研究室のボスは、特殊変数の表をふ〜んと2分ほど眺めて完全に暗記した。 > >そういうレベルの人たちのための言語なんだなと思って、トイレで血の涙を流した。 お助けください。 自作コードにおいて、以下の部分でmemsetの際にsegmentation faultになってしまいます。 int n = 2; char charList[n+1][32]; // 32文字の文字列をn+1確保・・・@ func(charList); // charListに書き込む関数 for (int i = 0; i < n; ++i) memset(charList[i], ' ', 32); // 空白で埋めたい funcを省いたりmemsetの前に適当にcharListをプリントしたりすると何故か問題なく動きます。 この部分だけ切り出したテストコードでは全く再現しませんでした。 この手のエラーでよくあるのは未初期化領域へのアクセスだと思いますが、 @で領域の確保はできているからこれは原因ではないですよね? どこがエラーの原因でしょうか・・・? 配列サイズ 32要素で 32個 空白で生めてるが 文字列 という用語を使ってるあたり func は一般の文字列操作してそうだけど 終端記号 '\0' の分の考慮が落ちてるんでないの? 「文字列」で32文字を取り扱いたいのなら 33要素の器が必要ってことで 文字の配列なら問題ないけどねー >>437 >char charList[10000][10000]; // 32文字の文字列をn+1確保・・・@ くらい確保して実行すれば動くだろ int n = 2; char charList[n+1][32]; C言語的に、これってOKだっけ? char charList[n+1,32]こうしないとダメだよね?なにかっこ二つ使ってんだよ読み込めねえよって昔2次元配列で怒られたな 最近どころか、20年近く前からだし >>448 みたいな文法は存在しないんだけど、どこの世界のC言語なんだろ iOS11にしたらまともに入力できない このクソOS ■ このスレッドは過去ログ倉庫に格納されています
read.cgi ver 07.5.5 2024/06/08 Walang Kapalit ★ | Donguri System Team 5ちゃんねる