- ベストアンサー
スレッドにて同一メモリの書き込み、読み取り
同一メモリに対してアクセスするスレッドA、Bがあった場合 同時書き込みをする様な処理については、間違いなく 排他制御が必要なのはわかっております。 1)一方が書き出しのみ、一方が読み取りのみの場合にもやはり、 排他制御は必須と思っていいのでしょうか? 2)また、排他制御について、 CriticalSection Mutex 上記くらいしか知らないのですが、 最も高速に動作するという事を考えた場合、どれがいいのでしょうか? (上記以外の別の仕組みがあれば教えて下さい。) 環境はVC6です。 よろしくお願いします。
- みんなの回答 (7)
- 専門家の回答
質問者が選んだベストアンサー
用途によりますが・・・というか、プラグラムできちんと約束事ができ、その約束を確実に守ることができるなら、排他制御が必要ない場合もある・・・と言っておきましょうか。 例えば、Aスレッドが書き込み担当、Bスレッドが読み込み専用として、共有メモリーは必ず先頭から末尾に向けてシーケンシャルに操作されるとします。AスレッドがBスレッドより「絶対に」早く処理がなされるという保証が別の機構によりあるのであれば、排他制御は不要です。 ただし、この「早く処理がなされる」と言うのは、Aスレッドの方が処理が少なく早いからではダメです。なぜなら、遅いはずのBスレッドにばかりCPUが割り当てられたらどうなるかという保証はアプリケーションでは制御不能だからです。Aスレッドの方が優先度がやたら高く設定してあるでもまだ不足です。Aスレッドが何らかのリソース待ちになったら(例えばHDDの読み取りとか)と考えたら、やはり制御不能です。 また、Aスレッドが何かを書いていようがいまいが、書いている最中であろうが、Bスレッドが読み込むのにお構いなしというのであっても排他制御は不要です。ただし、一つずつの整数を書いて読んでだから大丈夫とは解釈しないように。大きな整数は複数のバイトで構成されます。(intでさえ、1バイトではありません。)intが16ビットの構成として、上位8ビットを書き込んだ時点でBスレッドが上位下位の両方を読み取っていくかもしれません。 といった具合に、確かに、排他制御の不要なパターンは存在しますが、その判定は非常に難しい物があります。AスレッドとBスレッドのすべての動作パターンを慎重に考える必要がある上、そのパターンはすべて機械語レベルで考えなくてはなりません。しかも、OSがスレッドを切り替えるタイミングに対する深い知識が必要です。 というわけで、普通は、「排他制御は必須です。」と答えるのが実用的かと思います。つまり、そんなことを考えているくらいなら、最初からちゃんと排他制御を組んだ方が早くて確実と言うことです。 「排他制御をすると遅くなる」と言う場合は、その排他制御を無くしたことによって本当にソフトは早くなるのかの検証を忘れないでください。というのも、大概の場合、ちゃんと測定してみると他のボトルネックの方が遙かに大きいと言うことが普通だからです。(これは「コードを実行する時間の80%は、コード全体の20%で消費される」と表現されます。要するに、この20%がどこなのかを正確に把握しないと、最適化の努力は全くの無駄と言うことです。)
その他の回答 (6)
マルチコア、マルチプロセッサをちゃんと考えるならNo.6のような考え方はやめましょうね volatile付けるだけ、なんてのも当然
- rinkun
- ベストアンサー率44% (706/1571)
32bit Windows前提なら32bit intの書き込みはわざとアラインメントしないように配置するといった特別な場合を除きアトミックだから排他制御は不要だよ。 もちろん複数のintを一括書き込みしなければいけないような場合は排他制御が必要だけど。
- mitoneko
- ベストアンサー率58% (469/798)
環境がVC6と言うことは、ベースOSはwindowsですね。 しかも、おそらく、ハードウェア設計にまで踏み込む話ではありませんね? というのを前提にしておきます。(上記の前提が狂うと、この後の話も少し変わってきますので。まぁ、基本は同じなんですが・・・) スレッドの同時実行ですが、この「同時」という言葉には大きな誤解を招くことが往々にしてあります。 まず、普通のパソコンの構成を考えます。CPUは一個です。メインメモリーも一個です。 では、どうやって、複数のスレッドは同時に動くのか。 これをちゃんと理解しておかないと、「つまり、双方スレッドが同一メモリーに対して書き込みを行う様な場合、間違いなくエラーが発生しますが、」とか、「アプリが落ちるか否かをお聞きしたかったのですが、」なんて、こんな誤解が発生します。 人間の時間感覚ではなく、CPUの時間感覚で物事を考えましょう。CPUの時間感覚では、真の意味での同じ時間には一つの仕事しかできません。でも、1/1000秒、Aスレッドを実行して、次の1/1000秒にBスレッドを実行し、次の1/1000秒には、またAスレッドを実行・・・・を繰り返せば、人間の時間感覚では同時に実行しているように見えます。見えますが、あくまで、同じ時間には一つの仕事しかしないというのが基本です。これをタイムシェアリングといいます。(最近では、一つのCPUに2つのコアが入っていたり、2つのCPUが入っていたりと、真の意味での同時実行があり得ますが、これに伴う同時実行のインターロックはハードウェアが司りますので、ソフトウェアでは制御できませんし、考える必要もありません。最悪でもOSで考えることです。) というわけで、真の意味での同時に、同じメモリー領域に、違うスレッドがアクセスすると言うことはあり得ません。あくまで、順番・交代でアクセスするというモデルを考えてかまいません。 従ってたとえスレッドの排他制御を完全にサボっても「エラーが発生する」とか「アプリが落ちる」直接の原因にはなりません。例えば、読み込み信号と書き込み信号が配線の途中で激突してパソコンがフリーズした・・・というのは絶対にあり得ません。(これが発生したら、それはソフトのせいではありません。ハードウェア設計に問題があります。) 先のNo.1でも書いたように、「他の手法で、トラブルにならない保証がなされていれば、排他制御は不要です。」とはそういう意味です。 でも、当然、例えば、16ビットintの半分だけ書き込まれた、でたらめなint数値を元に、その数値を使って処理をすれば、その結果、「エラーが発生したり」、「アプリが落ちる」原因にはなり得ますね。そして、こうなってくれれば、大変運の良い、とてもたちの良いトラブルと言えます。少なくない場合、たまに発生する、正体不明のトラブルとなって現れます。例えば「たまに、表示される数値が間違っているんだけど」とか言ったパターンで。このソフトが会計ソフトだったら、決算時期になってきっと大騒ぎになるでしょう=^・・;= ちなみに、補足の質問で「・双方とも読み取り処理のみの場合=>???」に関しては、排他制御不要です。ただし、すべてのスレッドから読み取り専用であることが絶対条件です。なぜなら、先の説明の通り、真の意味で同時にアクセスすることはありませんから。読み取り専用なら、どんなに部分部分に分割されてばらばらに読まれても結果は必ず一緒です。
お礼
そういう事ですか。落ちるとかなんだとか直接的な原因ではなく、それを処理する後処理の過程で何かが不具合現象として発覚するという事ですね。よーくわかりました。2度にわたってありがとうございました。
- arain
- ベストアンサー率27% (292/1049)
No.2より ><現状の私の理解> >双方のスレッドが同一メモリーに対し >・双方とも書き込む可能性がある処理の場合=>当然ながら排他制御する。 >・一方書き込み、他方読み取りのみの場合=>これも排他制御する。 >・双方とも読み取り処理のみの場合=>??? > 上記二つは確かに排他制御が必要です。 最後については設計方針やシステム(特に組み込み系の)構成によります。 「読み出す」タイミングにおいて、別から「書き込まれ内容が変化する」ということを防ぐために排他制御が必要ですから、内容が変化することがないのだから排他制御はいらないと考えることができます。 一方で「資源の管理」という問題があります。これはたとえ「読み出し専用であろうとも使用している間は同時に複数からアクセスしてはいけない(できない)」というところから出てくる問題です。 このことを念頭に置いた場合には、読み出しのみであっても排他制御が必要となります。
>双方スレッドが同一メモリーに対して書き込みを行う様な場合、間違いなくエラーが発生しますが 疎結合のクラスタならともかく、変数が一命令で更新可能か否かにかかわらずマルチスレッドでエラーが発生することはありません >この時にintの値として、一瞬、むちゃくちゃな値になっても構わないという仕様でもしあるならば シーケンスロックですね
- arain
- ベストアンサー率27% (292/1049)
質問にはすでにNo.1氏が素晴らしい回答をされているので別の方に。 >という事ですが、この時にintの値として、一瞬、むちゃくちゃな値になっても構わないという仕様でもしあるならば、 (中略) >一方が読み取り専用であれば、こういう類のエラーは排他制御をするまでもなく、発生しないという理解でよろしいでしょうか? とは限りません。 ユーザーが排他制御を行うように、OSも自身で使用(管理)しているメモリについては排他制御を行います。また、物理的なメモリを管理しているチップセット自身も独自の管理を行っています。 それらがどのように管理されエラーとなるかは一般ユーザーのレベルでは判断できません(ドライバー作成者ならある程度OSの部分は理解しているだろうし、組み込み系の開発ならこの部分の理解はかなりの頻度で必須ですが)。 従って、「両方から書きこまなければエラーにならない」という保証はありません。 また、 >この時にintの値として、一瞬、むちゃくちゃな値になっても構わないという仕様でもしあるならば という前提でシステムを設計(プログラムを作成)することは非常に危険な行為です。 この「一瞬」と考えてる時間を正確に把握し、複数のスレッドのアクセス(使用)するタイミングをすべて把握しており、その全てにおいて問題なしと判断できれば別ですが。 同じメモリに対してどのようなタイミングで読み書きされているかを把握することは非常に困難です。 逆に考えれば「本当に一瞬なのか?」ということにもなりますし、「本当に必要なタイミング」で壊れたデータを読み出してしまうことも考えられます。 「たぶん大丈夫」という考えでの使用は、実際に問題が発生した場合に原因の特定を複雑にする要因になり非常に危険な行為です。
補足
アプリが落ちるか否かをお聞きしたかったのですが、 勿論、 >この時にintの値として、一瞬、むちゃくちゃな値になっても構わないという仕様でもしあるならば これでいいはずもなく、No.1の回答により、そうする(排他制御をしない。)気もありません。 では、スレッドA、Bが共に同一メモリー内を同時に読み取りする様なものだった場合は、どうなのでしょう。再度回答頂ければ幸いです。 <現状の私の理解> 双方のスレッドが同一メモリーに対し ・双方とも書き込む可能性がある処理の場合=>当然ながら排他制御する。 ・一方書き込み、他方読み取りのみの場合=>これも排他制御する。 ・双方とも読み取り処理のみの場合=>??? 以上、よろしくお願いします。
補足
よくわかりました。ありがとうございます。ところで、まさに一番気にしているところですが、 「上位8ビットを書き込んだ時点でBスレッドが上位下位の両方を読み取っていくかもしれません。」 という事ですが、この時にintの値として、一瞬、むちゃくちゃな値になっても構わないという仕様でもしあるならば、何らかのエラー(メモリーエラー?)になる事は無いという理解でよろしいでしょうか? つまり、双方スレッドが同一メモリーに対して書き込みを行う様な場合、間違いなくエラーが発生しますが、一方が読み取り専用であれば、こういう類のエラーは排他制御をするまでもなく、発生しないという理解でよろしいでしょうか? 再度、ご回答願えればと思います。