• ベストアンサー

マルチスレッドについて。

今、大きな配列を元に処理を行うプログラムを作成しています。 シングルスレッドでも十分速度を向上するようチューニングに成功しましたが、マルチスレッド化をすればさらに速度を向上させることができるだろうと考え、先日マルチスレッドかに成功しました。 しかし・・・奇妙な現象が起こりました。 マルチスレッドで性能を引き出すには、排他制御はないほうが良いと考え、メモリは食いますがスレッドに与える入力情報(大きな配列)を2つ用意し、排他制御なしの2スレッドを実行できるようにしました。しかしやはりメモリを消費しすぎてしまうため、配列にアクセスする部分のみ排他制御を行うようクリティカルセクションを設定し入力情報を2スレッドで共有して処理を行うよう組み替えました。 結果、やはり排他制御なしの場合よりはるかにスピードダウンしてしまい、シングルスレッドより少し早い処理時間で終了してしまいました。 余りにも悔しいため、ちょっと危険な実験だとは思いましたが、入力情報を2つのスレッドで共有しているにもかかわらず、排他制御の部分、つまりクリティカルセクションを取り除いて実行してみようと考えました。予想としては同時にアクセスし衝突が起きてエラーで停止してしまうと考えましたが・・・・・・ 結果なぜかエラーなく処理をし続け、普通に終了してしまいました。 これはなぜでしょう? 偶然にも共有情報に同時にアクセスすることがなかったためでしょうか?

質問者が選んだベストアンサー

  • ベストアンサー
  • MrBan
  • ベストアンサー率53% (331/615)
回答No.4

排他制御しないというよりは、同期/ロックしないのがコツというか、 単純にクリティカルセクションを取ったら唯のバグですから。 # ブレーキが邪魔だとブレーキ外してアクセルを踏むようなもの。 (小物は変に弄ってもかえって重くなることもあるので)規模にもよりますが、 その配列自体を二分割して最後に結合するようなアルゴリズム (処理中は排他する必要がなくなる)に変更するとか、 読み出し回数が圧倒的に多いならReaders/Writerロック作るとか、 リードオンリオブジェクトにするとか。そういう組み換えを考えられえてみては。 どう考えても、巨大な単一の配列を使ってる時点で、 マルチスレッド用に最適化された設計/アルゴリズムではありませんから。 目先/小手先でどうこうするより、まずアルゴリズムを練るのが常道かと。 # プロファイリングとかして見てますか。 # 後は、キャッシュヒット率とか考慮されてますか。

Mr_tenten
質問者

お礼

返答ありがとうございました。 実はマルチスレッドの概念を自分で勘違いしていました。 MrBan様のおっしゃられた通り、共有する配列は読み出しのみ行っていました。自分は共有する配列を同時に読みに行ってしまった場合衝突が起きるのではと考え、毎回の読み出しに排他制御を行っていました。 しかしまずマルチスレッドで同時に・・・なんてことは起こりませんよね。回答者No.2様がおっしゃられるように矛盾のみ発生するもので、同時になんてあるわけがないし、別に読み出しに排他制御は必要ありませんね。 今回の処理は入力の配列を複数のスレッドに渡し、共有変数への書き込みは行わない処理で、各スレッドはそれぞれ入力として与えられた配列から別々に結果を出力するプログラムなので、排他制御はまず不要と考えるべきでしょうね。MrBan様がおっしゃられる通り、巨大な単一の配列を共有して読み書きを行うとなるとそれはアルゴリズムを改良すべきでしょうね。 皆様返答ありがとうございました。

その他の回答 (3)

  • galluda
  • ベストアンサー率35% (440/1242)
回答No.3

がると申します。 ちょっとご自身のお立場が不明なので「お仕事でコーディングされている」前提とします。 > マルチスレッドで性能を引き出すには、排他制御はないほうが良いと考え これは確定でNGです。thread化したプログラムできちんと排他処理をしていないのは「エラーが出るプログラムです」と明記しているようなものなので。 性能云々以前の問題として、排他制御は「必ず」やってください。 > 結果なぜかエラーなく処理をし続け、普通に終了してしまいました。 > これはなぜでしょう? > > 偶然にも共有情報に同時にアクセスすることがなかったためでしょうか? 偶然、です。まぁ2threadくらいなら「たまたまぶつからなかった」んでしょう。 経験的には、2桁前半くらいなら、排他制御をしなくても(或いは雑な排他制御でも)「ある程度」動くみたいです。 3桁になるといきなりぶつかり出しますが。 いずれにしても、threadできちんと排他制御をせずに、というのは、あらゆる観点から「お勧めできない」ので。 趣味であればそれはそれで「面白い」と思いますが、お仕事であれば「絶対にやってはいけない」と思います。

Mr_tenten
質問者

補足

返答ありがとうございました。 失礼なので申し訳ないですが、疑問が解決したので、一応一番上の回答者様のお礼にまとめて記述します。

  • zwi
  • ベストアンサー率56% (730/1282)
回答No.2

それはたぶん、共有している情報が同時アクセスしてもハングアップにつながらない情報だったからでは無いでしょうか? 同じメモリアドレスに同時アクセスすると言っても、メモリにアクセスできるのは、瞬間的にはどちらかのスレッドだけです。 クリティカルセクションが必要な理由は、以下の様な場合です。 簡単にするためスレッドの処理は、 a++; だけです。 (1)int a = 1;でaが0x800000番地だったとします。 (2)番地0x800000からint値をCPUレジスタに読み出す(スレッドA) (3)スレッドA→B切り替え (4)番地0x800000からint値をCPUレジスタに読み出す(スレッドB) (5)CPUレジスタでint値に+1を行う(スレッドB) (6)番地0x800000にCPUレジスタのint値を書き出す(スレッドB) (7)スレッドB→A切り替え (8)CPUレジスタでint値に+1を行う(スレッドA) (9)番地0x800000にCPUレジスタのint値を書き出す(スレッドA) (10)printf("a=%d\n",a);(スレッドA) (11)スレッドA→B切り替え (12)printf("a=%d\n",a);(スレッドB) で結果は、どちらのスレッドもa=2と表示されます。 ちゃんと排他処理ができていればa=3となるべき処理ですよね。 ちゃんと処理できてませんが、エラーになるわけではありません。結果はちゃんと合っていますか?もし合っていたら単なる偶然だと思ってください。

Mr_tenten
質問者

お礼

返答ありがとうございました。 失礼なので申し訳ないですが、疑問が解決したので、一応一番上の回答者様のお礼にまとめて記述します。

  • MrBan
  • ベストアンサー率53% (331/615)
回答No.1

> 結果、やはり排他制御なしの場合よりはるかに > スピードダウンしてしまい、シングルスレッドより > 少し早い処理時間で終了してしまいました。 読んでいる限りでは、妥当だろうと納得できる結果です。 コードを見ていないので、ご自身も意図せず排他されてる、 または排他不要、読み出ししかしていない等の可能性も ゼロではないと思いますが、普通に考えてただの偶然です。 たまたま発生頻度が低かったか、発生しても害がないコードだったか、 害に発生に気づかなかったか、いずれかでしょう。 > 予想としては同時にアクセスし衝突が起きてエラーで停止してしまうと考えましたが・・・・・・ 配列の中身のデータに不整合が出た場合にそれを検出したり、 エラーを出したり、停止したりというコードを書いているのですか? それらがなくて、単純に同時アクセスが発生しただけなら、 衝突してもエラーなんて出ませんし、停止もしません。 スレッド間の排他制御はプログラマの責任です。 排他が漏れても、おかしな値のまま処理がそのまま動くだけですので、 「実は結果がおかしくなっている(が気づかなかった)」という可能性もありそうに思います。

Mr_tenten
質問者

お礼

返答ありがとうございました。 失礼なので申し訳ないですが、疑問が解決したので、一応一番上の回答者様のお礼にまとめて記述します。

関連するQ&A

  • マルチスレッドプログラミングの利点について

    はじめまして。 質問があります。 現在マルチスレッドプログラミングについて学習しているのですが、 学習を進めていくうちにある疑問が思いつきました。それは、 マルチスレッドで実装した方がよい場合とシングルスレッドで実装 した方がよい場合の区別がよくわからないということです。マルチスレ ッドで実装する利点は、例えば、応答性を向上させるとか、 入出力操作が関わっているときにCPUの空き時間を利用して別スレッド で処理を行わせることによってスループットを向上させることぐらいしか思いつきません。また、CPUが1個しかない環境では、 CPUをフル活動するようなプログラムでは、下手にマルチスレッドで 実装するよりもシングルスレッドで実装したほうが排他制御しない 分だけ処理スピードは上であると思っているのですが、間違いでしょうか?マルチスレッドで実装した場合の利点は上述したこと以外にも 何かあるのでしょうか?ご教授お願いします。

    • ベストアンサー
    • Java
  • マルチスレッド環境での配列使用について

    現在、C++を利用してマルチスレッド環境の開発を行っています。 マルチスレッドで排他的に変数を扱う場合、クリティカルセクションや ミューテックスを使用することで、排他制御をおこなうことができますが、 このように、配列の要素ごとに排他制御を行うことは可能でしょうか? たとえば、 int ary[10]; のような配列があれば、a[0]~a[9]まで要素をそれぞれ排他制御によって データの矛盾を避けたいと考えています。 パフォーマンスの関係上、できるだけ、配列全体をロックするのは避けたいと 思っています。 どうぞよろしくお願いします。

  • マルチスレッドについて

    現在”猫でもできる”の87、88章を学んでおります。 まず87章でマルチスレッドの根本的なやり方を学びましたが、いきなり疑問が浮かびました。 _beginthread関数によりスレッドをスタートさせ、この関数で登録した関数内で_endthread関数を実行し終了させていることは分かります。 しかし_beginthread関数で登録した関数に引数を渡す処理がどの部分で行われているのかわかりません。 登録する関数はvoid型で引数はvoid*型でなければいけないことは分かったのですが、プログラムのどこを見てもこの登録した関数に引数を渡す処理が行われていません。 その辺の動作の説明を分かる方でいいのでよろしくお願いします。 そして88章では排他制御のマルチスレッドを行うプログラムの製作を行っているのですが、ちょっとした疑問が浮かびました。 EnterCriticalSection関数、LeaveCriticalSection関数ではさまれたプログラムは排他制御され他からアクセスされない。 この関数はこんな理解で良いんですかね? この理解で行くと、88章で説明していきますが、子ウィンドウを2つ作成しそれぞれのプロシージャ内で排他制御された関数をスレッドとしてスタートしています。 この2つのスレッドの動作についてですが、互いに排他制御関数が記述されているため、動作としてはまず左の子ウィンドウのスレッドが処理されている場合、右の子ウィンドウのスレッドは停止している。そして左の子ウィンドウのスレッドの排他制御が解放されたときに、右の子ウィンドウのスレッドが開始する。 そしてあるとき左の子ウィンドウのクライアントウィンドウ内で右クリックされた場合、その時点で排他制御されたスレッドが終了するのを待ち、終了したらcountを+1する。 こんな動作が行なわれていると理解してよいのでしょうか?

  • C++マルチスレッド処理について

    こんにちは 現在スレッドを作成して,マルチスレッド処理をしようとしているのですが, 全然理解できないので皆様のお力をお貸し下さい。 やりたい事は, (1) メインスレッドからスレッド1を作成。 (2) スレッド1では,ひたすらファイルなどからデータを取得させる。(読み込めなくなるまで) (3) メインスレッドでは,時々スレッド1を止めて,再度処理を続行させたい。 class Sample { public: // Sampleオブジェクト作成,同時にスレッド1を作成 Sample* create(); // スレッド1を止めて,再度動かす void process(); private: // この関数をスレッド1で処理させる // 内部では,読み込めなくなるまで無限ループ? void get(); }; スレッドに関してはイメージが湧くのですが, mutex (必要ですか?) に関しては一向に理解できません。 何を排他制御するのでしょうか。オブジェクト? 関数? 複雑ではなさそうなのですが,今までシングルスレッドの処理のみ書いていたため ピンときません。 よろしくお願い致します。

  • 「UIスレッド」「マルチスレッド」の違い

    Androidで、「UIスレッド」「マルチスレッド」の違い、を教えてください。 ■下記理解で合ってるでしょうか? Androidは、「シングルスレッド」の「UIスレッド」モデルなので、それを「マルチスレッド」で動かそうとすると、「ワーカースレッド」で処理する必要がある ・「マルチスレッド」=「マルチタスク」? ・「UIスレッド」とは、メインスレッドが「GUI (グラフィカルユーザーインターフェイス) 処理」を行うものを指す? ・つまり、「Android」=「UIスレッド」? ・「UIスレッド」って何の用語? Androidに特化した用語? それともJAVAの用語? あるいはそれ以外?

    • ベストアンサー
    • Java
  • マルチスレッド化。

    今とても大きな配列を使用し、長時間処理をするプログラムを組んでいます。元々処理時間に1時間を要するプログラムを2分で処理を完了するまでに仕上げました。 しかし、探究心はおさまらずもう少し高速化に挑みたいと考えています。 過去に「猫でもわかる」のSDK第1章と2章を学び、マルチスレッドのプログラムをSDKで組んだことがあります。それを利用してマルチスレッド化を実現したいと考えています。 言語はCでVisualStudio2005を使用しています。 *疑問1   SDKの場合WinMain関数とプロシージャからの実行で_beginthread関数を記述すれば処理が開始されます。 Cでもmain関数内に記述すれば、SDKと同様に処理できるのでしょうか? *疑問2 _beginthread関数の引数に関してです。 第1引数にvoid型のスレッド関数、第2引数に0?、そして第3引数にはスレッド関数に渡すデータの引数を記述すると把握しているのですが、渡したいデータは複数あり、***型と**型、それに変数を数個とスレッド関数に渡したいデータだらけなのですが、どのように記述すればよいでしょう? *疑問3 2つのスレッドを作成しようと考えていますが、その2つのスレッドで1つの大きな配列を共有して処理したいと考えています。 そのため、スレッド間の同期が必要になるわけですが同期の種類にもクリティカルセクションやミューテックス、イベントと豊富でどれを使用すれば良いのか迷ってしまいます。厳密に同期を取り処理をするにはイベントが一番良いと考えています。 これらの疑問に答えられる方はアドバイスをよろしくお願いします。

  • Xcodeでマルチスレッドでなく順処理する方法

    こんにちは。 今Xcode4.2.1を使っております。 さて、今処理としてあるアプリ連携を行う処理を作っているのですが、 マルチスレッドでなく 「処理A」→「処理B」→「処理C」 と順に処理をしたいのですが、スレッド化してうまくいきません。 具体的には [self presentModalViewController: controller animated: YES];(処理A) [処理B] [処理C] : と書いた時に、処理Aがスレッドとして動くので、 処理Aが終了しないうちにB、Cがスタートしてしまいます。   1)順に処理をさせたい(マルチスレッド化しない方法)のですが、     どうやったらよいのでしょうか? もしくは   2)Aの処理がおわるまで監視&制御する(排他制御)方法はあるのでしょうか? 素人質問で申し訳ありません。 些細なことでも結構ですので 宜しくお願い致します。

  • マルチスレッドプログラミングの参考書

    スレッドやプロセスなどの意味は理解しているつもりです。 実際プログラミングをする技術を身につけたいと思っていますが おすすめの、勉強(参考)となる書籍はありますでしょうか? プラットフォームはLinux系です。 pthreadやselectを使用できればよいです。 最終的にどんなことがしたいかといいますと マルチスレッド対応したソケットプログラムを 作ってみたいのです。 シングルスレッドであれば、ある程度作れるのですが マルチスレッドではどのように作成していいのか・・・ 参考になりそうなHPでもいいのでよろしくお願いします。

  • デュアルコアCPUはマルチスレッドに最適?

    デュアルコアCPUはマルチスレッドに有効と聞きますが、マルチスレッドとはどういうものでしょうか? (1)単純に、3Dゲームをし、ネットをし、同時にエンコードをし、同時に画像処理し・・・っていう作業の「マルチ」ですか? (2)それとも、単独アプリケーション自体にマルチスレッド対応(?)のものがあるのですか? よく、3Dゲームはシングルスレッドのため、デュアルコアCPUの優位性を引き出せないといいますが、上記(1)の場合に当てはまりますか?

  • MFCマルチスレッドについて

    MFCマルチスレッドについて COMやIOボードからの入力に応じて動作するアプリを作っています。 AfxBeginThreadにてそれぞれワーカスレッドを作成しCOMやIOから入力があれば AfxBeginThreadを呼んでいるクラスにあるメンバ関数を実行しようとしています。 AfxBeginThreadにて*thisを送り、制御関数内で、mycls->OnButton***()というような 感じで現在は作っています。(OnButton***になっているのはデバッグ用にボタンで あらかじめ作成している関数のためです。) このときに、mycls->OnButton***()は親スレッドで動いていると考えていいのですか? あくまで親スレッドのクラスのメンバ関数を制御関数が動いている子スレッドで実行 しているだけなのでしょうか? 実は、ログ表示のため制御関数の中(受信データを表示)と、mycls->OnButton***()の中 (作業結果を表示)に同じエディットコントロールへの表示部分があります。 表示部分の処理は、いったんCStringで読み込んできて最大文字数チェックを行い、 再度文字数を調節して書き直しということをやっているため、一応クリティカル セクションにはしているのですが、実際どう動いているか分からないため、やり忘れ ていることや、やってはいけないことをやってそうです。 すいませんがご教授願います。

専門家に質問してみよう