• ベストアンサー

スレッドにて同一メモリの書き込み、読み取り

同一メモリに対してアクセスするスレッドA、Bがあった場合 同時書き込みをする様な処理については、間違いなく 排他制御が必要なのはわかっております。 1)一方が書き出しのみ、一方が読み取りのみの場合にもやはり、   排他制御は必須と思っていいのでしょうか? 2)また、排他制御について、 CriticalSection Mutex 上記くらいしか知らないのですが、 最も高速に動作するという事を考えた場合、どれがいいのでしょうか? (上記以外の別の仕組みがあれば教えて下さい。) 環境はVC6です。 よろしくお願いします。

  • Vargas
  • お礼率85% (174/204)

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

  • ベストアンサー
  • mitoneko
  • ベストアンサー率58% (469/798)
回答No.1

 用途によりますが・・・というか、プラグラムできちんと約束事ができ、その約束を確実に守ることができるなら、排他制御が必要ない場合もある・・・と言っておきましょうか。  例えば、Aスレッドが書き込み担当、Bスレッドが読み込み専用として、共有メモリーは必ず先頭から末尾に向けてシーケンシャルに操作されるとします。AスレッドがBスレッドより「絶対に」早く処理がなされるという保証が別の機構によりあるのであれば、排他制御は不要です。  ただし、この「早く処理がなされる」と言うのは、Aスレッドの方が処理が少なく早いからではダメです。なぜなら、遅いはずのBスレッドにばかりCPUが割り当てられたらどうなるかという保証はアプリケーションでは制御不能だからです。Aスレッドの方が優先度がやたら高く設定してあるでもまだ不足です。Aスレッドが何らかのリソース待ちになったら(例えばHDDの読み取りとか)と考えたら、やはり制御不能です。  また、Aスレッドが何かを書いていようがいまいが、書いている最中であろうが、Bスレッドが読み込むのにお構いなしというのであっても排他制御は不要です。ただし、一つずつの整数を書いて読んでだから大丈夫とは解釈しないように。大きな整数は複数のバイトで構成されます。(intでさえ、1バイトではありません。)intが16ビットの構成として、上位8ビットを書き込んだ時点でBスレッドが上位下位の両方を読み取っていくかもしれません。  といった具合に、確かに、排他制御の不要なパターンは存在しますが、その判定は非常に難しい物があります。AスレッドとBスレッドのすべての動作パターンを慎重に考える必要がある上、そのパターンはすべて機械語レベルで考えなくてはなりません。しかも、OSがスレッドを切り替えるタイミングに対する深い知識が必要です。  というわけで、普通は、「排他制御は必須です。」と答えるのが実用的かと思います。つまり、そんなことを考えているくらいなら、最初からちゃんと排他制御を組んだ方が早くて確実と言うことです。  「排他制御をすると遅くなる」と言う場合は、その排他制御を無くしたことによって本当にソフトは早くなるのかの検証を忘れないでください。というのも、大概の場合、ちゃんと測定してみると他のボトルネックの方が遙かに大きいと言うことが普通だからです。(これは「コードを実行する時間の80%は、コード全体の20%で消費される」と表現されます。要するに、この20%がどこなのかを正確に把握しないと、最適化の努力は全くの無駄と言うことです。)

Vargas
質問者

補足

よくわかりました。ありがとうございます。ところで、まさに一番気にしているところですが、 「上位8ビットを書き込んだ時点でBスレッドが上位下位の両方を読み取っていくかもしれません。」 という事ですが、この時にintの値として、一瞬、むちゃくちゃな値になっても構わないという仕様でもしあるならば、何らかのエラー(メモリーエラー?)になる事は無いという理解でよろしいでしょうか? つまり、双方スレッドが同一メモリーに対して書き込みを行う様な場合、間違いなくエラーが発生しますが、一方が読み取り専用であれば、こういう類のエラーは排他制御をするまでもなく、発生しないという理解でよろしいでしょうか? 再度、ご回答願えればと思います。

その他の回答 (6)

noname#208124
noname#208124
回答No.7

マルチコア、マルチプロセッサをちゃんと考えるならNo.6のような考え方はやめましょうね volatile付けるだけ、なんてのも当然

  • rinkun
  • ベストアンサー率44% (706/1571)
回答No.6

32bit Windows前提なら32bit intの書き込みはわざとアラインメントしないように配置するといった特別な場合を除きアトミックだから排他制御は不要だよ。 もちろん複数のintを一括書き込みしなければいけないような場合は排他制御が必要だけど。

  • mitoneko
  • ベストアンサー率58% (469/798)
回答No.5

 環境が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数値を元に、その数値を使って処理をすれば、その結果、「エラーが発生したり」、「アプリが落ちる」原因にはなり得ますね。そして、こうなってくれれば、大変運の良い、とてもたちの良いトラブルと言えます。少なくない場合、たまに発生する、正体不明のトラブルとなって現れます。例えば「たまに、表示される数値が間違っているんだけど」とか言ったパターンで。このソフトが会計ソフトだったら、決算時期になってきっと大騒ぎになるでしょう=^・・;=  ちなみに、補足の質問で「・双方とも読み取り処理のみの場合=>???」に関しては、排他制御不要です。ただし、すべてのスレッドから読み取り専用であることが絶対条件です。なぜなら、先の説明の通り、真の意味で同時にアクセスすることはありませんから。読み取り専用なら、どんなに部分部分に分割されてばらばらに読まれても結果は必ず一緒です。

Vargas
質問者

お礼

そういう事ですか。落ちるとかなんだとか直接的な原因ではなく、それを処理する後処理の過程で何かが不具合現象として発覚するという事ですね。よーくわかりました。2度にわたってありがとうございました。

  • arain
  • ベストアンサー率27% (292/1049)
回答No.4

No.2より ><現状の私の理解> >双方のスレッドが同一メモリーに対し >・双方とも書き込む可能性がある処理の場合=>当然ながら排他制御する。 >・一方書き込み、他方読み取りのみの場合=>これも排他制御する。 >・双方とも読み取り処理のみの場合=>??? > 上記二つは確かに排他制御が必要です。 最後については設計方針やシステム(特に組み込み系の)構成によります。 「読み出す」タイミングにおいて、別から「書き込まれ内容が変化する」ということを防ぐために排他制御が必要ですから、内容が変化することがないのだから排他制御はいらないと考えることができます。 一方で「資源の管理」という問題があります。これはたとえ「読み出し専用であろうとも使用している間は同時に複数からアクセスしてはいけない(できない)」というところから出てくる問題です。 このことを念頭に置いた場合には、読み出しのみであっても排他制御が必要となります。

noname#208124
noname#208124
回答No.3

>双方スレッドが同一メモリーに対して書き込みを行う様な場合、間違いなくエラーが発生しますが 疎結合のクラスタならともかく、変数が一命令で更新可能か否かにかかわらずマルチスレッドでエラーが発生することはありません >この時にintの値として、一瞬、むちゃくちゃな値になっても構わないという仕様でもしあるならば シーケンスロックですね

  • arain
  • ベストアンサー率27% (292/1049)
回答No.2

質問にはすでにNo.1氏が素晴らしい回答をされているので別の方に。 >という事ですが、この時にintの値として、一瞬、むちゃくちゃな値になっても構わないという仕様でもしあるならば、 (中略) >一方が読み取り専用であれば、こういう類のエラーは排他制御をするまでもなく、発生しないという理解でよろしいでしょうか? とは限りません。 ユーザーが排他制御を行うように、OSも自身で使用(管理)しているメモリについては排他制御を行います。また、物理的なメモリを管理しているチップセット自身も独自の管理を行っています。 それらがどのように管理されエラーとなるかは一般ユーザーのレベルでは判断できません(ドライバー作成者ならある程度OSの部分は理解しているだろうし、組み込み系の開発ならこの部分の理解はかなりの頻度で必須ですが)。 従って、「両方から書きこまなければエラーにならない」という保証はありません。 また、 >この時にintの値として、一瞬、むちゃくちゃな値になっても構わないという仕様でもしあるならば という前提でシステムを設計(プログラムを作成)することは非常に危険な行為です。 この「一瞬」と考えてる時間を正確に把握し、複数のスレッドのアクセス(使用)するタイミングをすべて把握しており、その全てにおいて問題なしと判断できれば別ですが。 同じメモリに対してどのようなタイミングで読み書きされているかを把握することは非常に困難です。 逆に考えれば「本当に一瞬なのか?」ということにもなりますし、「本当に必要なタイミング」で壊れたデータを読み出してしまうことも考えられます。 「たぶん大丈夫」という考えでの使用は、実際に問題が発生した場合に原因の特定を複雑にする要因になり非常に危険な行為です。

Vargas
質問者

補足

アプリが落ちるか否かをお聞きしたかったのですが、 勿論、 >この時にintの値として、一瞬、むちゃくちゃな値になっても構わないという仕様でもしあるならば これでいいはずもなく、No.1の回答により、そうする(排他制御をしない。)気もありません。 では、スレッドA、Bが共に同一メモリー内を同時に読み取りする様なものだった場合は、どうなのでしょう。再度回答頂ければ幸いです。 <現状の私の理解> 双方のスレッドが同一メモリーに対し ・双方とも書き込む可能性がある処理の場合=>当然ながら排他制御する。 ・一方書き込み、他方読み取りのみの場合=>これも排他制御する。 ・双方とも読み取り処理のみの場合=>??? 以上、よろしくお願いします。

関連するQ&A

  • セマフォとmutexの違いは?

    排他制御としてセマフォとmutexがありますが、 この二つの違いがよくわかりません。 自分で調べてみたところ、 ・セマフォ…プロセス間排他制御。複数ロックがかけられる。 ・mutex…スレッド間排他制御。ロックは一つだけ。 と言うような違いがあるようなのですが、これだけの差なんでしょうか? (これだけの差、と言ってる時点で筋違いだったら申し訳ありません) また、セマフォをスレッド間排他制御に用いたり、 mutexをプロセス間排他制御に用いることは可能なのでしょうか? 可能だとしたら、これらが2種類存在する理由も教えていただきたいです。

  • ソフトの多重起動制限

    以前知り合いに作ってもらったWindow用のソフトを愛用してるのですが 同時に3個までしか起動できず、4個目を起動すると3個起動してるので動きませんとメッセージが出ます。 その知り合いとはもう疎遠になってしまったので直してもらうことが出来ません。 VC++ mutex で排他制御を行っていることまではわかったのですが バイナリエディタでこのソフトの中身を書き換えて同時起動の回数を変えることは出来ますでしょうか? また、出来る場合はどこをどのように変えればいいかというのは簡単にわかるものでしょうか? よろしくお願いします。 ※もらった時に自己責任で改造は自由にという許可はもらってあります。

  • wait()したスレッドが起こされるタイミング

    以下のコードにおいて。 % java ThreadWaitTest 2 の場合はaと表示したまま止まりますが、 % java ThreadWaitTest 1 の場合はスレッドの終了に伴ってwait()が切れ、bまで表示します。 私の望む動作は前者です。 後者の場合、これはつまりスレッドをwaitした際の待機プールがスレッドオブジェクトの場合、そのスレッドの実行が終了すると待機プールにある待機スレッドを自動でnotify()してしまうことを意味していると思うのですが、この解釈はあっていますか? Java のAPIドキュメントに 「別のスレッドが notify メソッドまたは notifyAll メソッドを呼び出してこのオブジェクトのモニター上で待機するスレッドに通知を出すまで待機します」 とあるように、ユーザが明確的にnotifyしないと起こらないと思いこんでいました。 なぜこういう仕様になっているのでしょうか? また、この仕様について詳しく書いてあるサイトやドキュメントなどありませんでしょうか? public class ThreadWaitTest extends Thread {  public Object mutex = new Object();    public static void main(String[] args) {   try {    ThreadWaitTest test = new ThreadWaitTest();    test.start();    System.out.println("a");    switch (new Integer(args[0])) {    case 1:     synchronized(test) {      test.wait();     }     break;    case 2:     synchronized(test.mutex) {       test.mutex.wait();     }     break;    }    System.out.println("b");   } catch (InterruptedException ie) {    ie.printStackTrace();   }  }  public void run() {   try {    Thread.sleep(5000);   } catch (InterruptedException ie){    ie.printStackTrace();}   } }

    • ベストアンサー
    • Java
  • マルチスレッドについて

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

  • Javaスレッドの割り込みについて

    Javaのスレッド割り込み制御についてご質問です。 スレッドの割り込みの際に用いるメソッドinterrupt()は、 通常、対象のスレッドがsleep()やwait()状態の最中でアクセス不能な場合に使用しますが、 対象のスレッドが、sleep()やwait()以外の状態で、正常に動作している場合に interrupt()を実行した場合、どのような動きをするのでしょうか? 特に問題などありますでしょうか? お手数ですが、どなたかご教授願います。

  • VC++スレッドの正しい終了のさせかた

    VC++6.0にてAfxBeginThreadで m_bAutoDelete = TRUEにてスレッドをおこしております。 この終了時に制御関数のwhileループを脱する様にし、 正常にスレッドを終了させているつもりです。 この後、再度(アプリは継続して起動したまま) AfxBeginThreadにて全く同じ処理で再開すると、 なぜか、前のスレッドが未だ動作しているかのごとく 制御関数内のTRACEが2重に出力されます。 再度、停止し、またスレッド起動すると、 今度は3重になったかの様な動作をします。 スレッドが正しく終了されていないのでは?と思った現象として、 1回起動時にアプリを終了させると正常終了しますが、 2回起動以上は必ずスレッドのメモリーリークが出ます。 メモリーリーク個所はAfxBeginThreadでした。 制御関数内で必要ないとは思いましたが、終了時に AfxEndThreadを使用しましたが現象は同じでした。 そこで質問です。 1)この現象は、スレッドが正常に終了されていない事に起因しているのでしょうか? 2)スレッドを正しく終了させるにはどうすればいいのでしょうか? 当方、制御関数がループを抜け、さらにm_bAutoDelete = TRUEであれば オブジェクトも自動的に破棄されると思っていたのですが。。。 以上、よろしくお願いします。

  • pthread_mutex_unlock失敗?

    pthreadプログラムで、mutex_lockするスレッドとmutex_unlockするスレッドが別スレッドとなっている場合、mutex_unlockが失敗することはあるでしょうか。mutexの初期設定(mutex_init)時、スレッドの属性は特に指定していません。 OSはHP-UXでCでコードを記述しています。 単発のプログラムで動作確認をした限りでは無事にunlockされているようでした。 (lockスレッドとunlockスレッドが別ということ自体が少し危険なことだとは思うのですが・・・)

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

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

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

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

  • セマフォとスレッドの問題について

    この問題は東京大学大学院の問題なのですが、問題の求めているものがよくわかりません。 セマフォとスレッドなどは勉強して理解しているのですがこの問題はどのようにして解くのでしょうか? 8つのスレッドT1,T2,・・・,T8がある。それぞれのスレッドTiはステートメントSi()を実行するが、Sj()に関連付けされたセマフォSjのオペレーションP(Sj),V(Sj)などを利用して同期・排他制御を行う。(i,j=1,・・・,8)。ステートメントSi間の実行順序に添付した図のような依存性がある場合、それぞれのスレッドTiが実装する同期方法をSi(),P(Sj),V(Sj)などを用いて説明せよ(i,j=1,・・・,8)。 例えば、S1()はS2()とS3()より前に実行する必要があるが、S2()とS3()は同時実行可能である。また、S7()はS4()とS5()が終了しないと実行できない。全てのセマフォは初期化されていると仮定してよい。