こんにちは。今日も並列的なインドリです♪
突然ですが今日も並列処理について囀ります♪
並列処理の一番難しいところは、実行順序が決まっておらず、何時他の処理からの影響を受けるか分からない点です。
例えば、
flag = true; //※flag変数は外部から変えられると仮定する
if ( flag == true ) {
//ここに来るよね?
} else {
//ここに来る事はないはずだけど・・・
}
このプログラムでも、false時の処理がされる場合があります。逐次処理に慣れきった人は、ここで足元をすくわれます。
ですが、複数の処理を並列的に行っているのですから、flagを変える処理が割り込めば、falseになる可能性があるのです。
こんな単純な処理ですら、逐次処理の感覚でしていれば罠にはまります。
しかもこの問題は、短いサンプルでは表面化しません。
従って、逐次処理では短いプログラムを実行し、エラーの有無を確かめる事は有用ですが、並列処理では通用しません。
では、どうすればいいのかと言いますと、純粋に知識と経験から判断するしかありません。
並列処理の知識があれば、先ほどのサンプルの問題に気が付きます。
といっても、本来技術者は自分のプログラムを把握するべきですから、知識と経験が必要なのは当然です。
とはいえ、問題がないわけではありません。
それは、テストプログラムが組みにくい点です。
短いテストプログラムでテストをパスしても、実稼働した時に問題が発生する可能性があります。
これを防ぐには、全技術者が知識を高める努力をし、コードレビューをするしかありません。
しかしながら、それだけでは工学的に問題を解決したとは言い切れないと考えます。
理想的には、flagをfalseに設定する様な賢いテストプログラムが必要です。
これはチェックツールである程度可能ですが、難しいケースとなると不十分だと感じます。
テスト用の人工知能を組み込む必要性があります。
さて、先ほどのプログラムの問題を解決するにはどうすればいいと思いますか?
その答えは、「flagをローカル変数にする」か「ロックを掛ける」です。
このうちどちらが望ましいかといいますと、ロックはパフォーマンスを低下させるので、ローカル変数にする方です。
それ故、スレッドローカルストレージが多用されているわけです。
この答えはなれない人にとっては難しく感じるかもしれません。
ですが、オブジェクト指向では、口うるさく「変数を外部に出すな」とカプセル化の原則を言われます。
それを実践していれば、こういう風な問題に遭遇する可能性が低くなります。
そういった事からオブジェクト指向を実践する事は、並列処理にとっても有益です。
情報処理技術は全てが結びついており、不必要な知識なんてないのです。
この春入社する新人さんはよく学び、よく遊びましょう♪
面白いですよー♪
テーマ : プログラミング
ジャンル : コンピュータ
>flag = true; //※flag変数は外部から変えられると仮定する
>その答えは、「flagをローカル変数にする」か「ロックを掛ける」です。
>このうちどちらが望ましいかといいますと、ロックはパフォーマンスを低>下させるので、ローカル変数にする方です。
ローカル変数にするということは、最初の仮定が成り立たなくなるよ。
>口うるさく「変数を外部に出すな」
これには同意。
> >flag = true; //※flag変数は外部から変えられると仮定する
>
> >その答えは、「flagをローカル変数にする」か「ロックを掛ける」です。
> >このうちどちらが望ましいかといいますと、ロックはパフォーマンスを低>下させるので、ローカル変数にする方です。
>
> ローカル変数にするということは、最初の仮定が成り立たなくなるよ。
うーん。どうしてそう考えるのかな?
問題があるプログラムを提示して、その問題を解決するために、ローカル変数にするべきだと言っているわけです。
並列処理では、実行順序が決まっておらず、何時他の処理からの影響を受けるか分かりません。
従って、変数を下手に外部に出すとエラーの元になります。
それを理解せず、逐次処理の感覚で短いサンプルを作って、
「動いているから正しい」と言い張る人が居ます。
しかしそれは、ただ単にサンプルが短すぎて、他のスレッドからの影響を受けていないだけであって、行ってはならない致命的な誤りです。
無闇に変数を外部に出してはなりません。
それは、何も並列処理に限った話ではなく、オブジェクト指向でも言われている事です。
並列処理を難しいと拒否する人もいますが、並列処理の考え方も無から生じたわけではなく、
元からある概念の積み重ねで出来ています。
それ故、身構えずに気楽に広く学べばいいわけです。
>問題があるプログラムを提示して、その問題を解決するために、ローカル変数にするべきだと言っているわけです。
その場合、外部で使っていた方は「未定義」になってしまいますが?
> >問題があるプログラムを提示して、その問題を解決するために、ローカル変数にするべきだと言っているわけです。
>
> その場合、外部で使っていた方は「未定義」になってしまいますが?
普通はアクセス範囲を局所的にする事を狙います。
だから、設計を見直せと言っているわけです。
というか、未定義ではなくて、コンパイルエラーが出るよ。
変数のアクセス範囲を慎重に見極めましょう。
基本的に極力アクセス範囲を狭くする方がいいです。
逐次処理の感覚で、なんとなく変数を扱っていると、並列処理をするのは困難になります。
横槍ごめん。
>普通はアクセス範囲を局所的にする事を狙います。
>だから、設計を見直せと言っているわけです。
あるまじろさんは設計上、アクセス範囲を小さくしていいのか?という事を聞いているんだと思います。
たぶん元のサンプルにあるコメント
> //※flag変数は外部から変えられると仮定する
が本来の仕様なのか、想定外の動作なのかがはっきりしない所為でしょう。
本来の仕様:
タイミングの問題を別にすれば、外部から flag の値を変えられる事自体は正しい動作
想定外の動作:
flag の値は本来外部から変えられてはいけないのに、何らかの理由で外部から変えられてしまっていた
記事中のサンプルはどちらのつもりで提示したサンプルなんでしょう?
> 横槍ごめん。
>
> >普通はアクセス範囲を局所的にする事を狙います。
> >だから、設計を見直せと言っているわけです。
>
> あるまじろさんは設計上、アクセス範囲を小さくしていいのか?という事を聞いているんだと思います。
> たぶん元のサンプルにあるコメント
>
> > //※flag変数は外部から変えられると仮定する
>
> が本来の仕様なのか、想定外の動作なのかがはっきりしない所為でしょう。
>
> 本来の仕様:
> タイミングの問題を別にすれば、外部から flag の値を変えられる事自体は正しい動作
>
> 想定外の動作:
> flag の値は本来外部から変えられてはいけないのに、何らかの理由で外部から変えられてしまっていた
>
> 記事中のサンプルはどちらのつもりで提示したサンプルなんでしょう?
どちらも想定して書いています。
だから、「ローカル変数にする」か「ロックを掛ける」の2つの答えを用意しています。
もし片一方だけ想定するのであれば、両方書きません。
ただし、大概flagの様なステータス系変数が外部からアクセスされる時は、並列処理は動作しません。
というのも、複数の処理が同時にそのオブジェクトにアクセスして、ステータスを変えてしまうからです。
そうなると、予期しない動作を起こすでしょう。
よく読めばわかると思いますが、無闇に変数を外部に出すなと言っているわけですから、どちらかというと、出したら駄目な例なのです。
変数を外部に出してもよい例を出して出すなという人はいません。
それに加えて、慎重に変数のアクセス範囲を見極めるように言っているわけですから、
その都度判断をすればいいという事です。
無闇に変数をローカルにするとか、無闇にロックを掛けるのは駄目なのです。
他にも色々ありますが、先ずはこの2つを理解しないと先に進めないでしょう。
念のために付け加えて置きますが、よいオブジェクト指向をしていれば、意味もなく変数を外部に出すという事はしないはずです。
何故ならば、カプセル化の原則は、データの隠ぺいだけではなく、
再利用の為に余計な情報を外部に出さない事を意味しているからです。
つまり、並列処理をするからと言ってオブジェクト指向をないがしろにしてもいいというわけではなく、並列処理とオブジェクト指向の知識は両方いります。
ちなみに、flagとかで動作を変えるぐらいならば、オブジェクト指向では通常、違うメソッドにするでしょう。
状況によって対処法は異なりますが、継承やオーバーライドを駆使する事になるでしょう。
まれにフラグを使わないと駄目な処理もありますが、大体はフラグを多用し始めたら危険な兆候であり、リファクタリングの帽子をかぶる事になります。
>> 本来の仕様:
>> タイミングの問題を別にすれば、外部から flag の値を変えられる事自体は正しい動作
>> 想定外の動作:
>> flag の値は本来外部から変えられてはいけないのに、何らかの理由で外部から変えられてしまっていた
>> 記事中のサンプルはどちらのつもりで提示したサンプルなんでしょう?
>どちらも想定して書いています。
>だから、「ローカル変数にする」か「ロックを掛ける」の2つの答えを用意しています。
どちらも想定、ですか...正直予想外でした。
変数のアクセス範囲は並列処理でなくても考える事だし、並列処理の話でもあるので基本的には仕様かと考えていました。
>もし片一方だけ想定するのであれば、両方書きません。
...何の前置きもなく単に両方の答えを書いただけで、私が例示した二種類のケースをどちらも想定していると考えられる技術者はほぼ皆無だと思いますよ...
>ただし、大概flagの様なステータス系変数が外部からアクセスされる時は、並列処理は動作しません。
>というのも、複数の処理が同時にそのオブジェクトにアクセスして、ステータスを変えてしまうからです。
>そうなると、予期しない動作を起こすでしょう。
え、並列処理時に flag のような外部からも操作できる変数を安全にアクセスする為に使うのがロックですよね?
で、ご自身でも「ロックを掛ける」のは答えの一つだとかいていらっしゃいますよね?
なのに「大概flagの様なステータス系変数が外部からアクセスされる時は、並列処理は動作しません。」て続けられても、何を言いたいのかさっぱり分かりません。
いくらロックを掛けてもステータスが変わるタイミングは予期できないから、
「大概flagの様なステータス系変数が外部からアクセスされる時は、並列処理は (ロックを掛けても期待通りの順番では) 動作しません。」という事?
それともロックを掛けないと安全には動作させられないから
「大概flagの様なステータス系変数が外部からアクセスされる時は、(ロックを掛けないと) 並列処理は動作しません。」という事?
それともそれ以外の何か?
>無闇に変数をローカルにするとか、無闇にロックを掛けるのは駄目なのです。
「無闇にロックを掛けるのはダメ」には賛成です。
>念のために付け加えて置きますが、よいオブジェクト指向をしていれば、意味もなく変数を外部に出すという事はしないはずです。
ええと、並列処理なら並列処理に絞って話をしていただけると助かります。
オブジェクト指向にしても変数のアクセス範囲にしても、別に並列処理に特有の考え方ではありませんし、あれもこれもと欲張ると話の焦点がボケてしまいますので。
うーん。どこが分からないのだろう?
正直言って、何が分からなくてそんな事を言っているのかが分かりません。
記事とコメントを読んでほしいな。記事とコメントに書いてある事を何度も問われても・・・
これは極めて単純な話しで、並列処理の原則は、変数のアクセス範囲を極力狭くする事です。
ロックを掛けるのはその後です。
何でもロックすればいいというものではありません。
それと、オブジェクト指向の知識も必要です。
オブジェクト指向言語(大概がそう)で並列処理をする場合、当然オブジェクト指向についても考えなくてはなりません。
並列処理だからと言って、オブジェクト指向的におかしな事をするのは問題外です。
オブジェクト指向がちゃんとした上で、並列処理をしないと駄目です。
並列処理の基礎的を解説するのが目的だから、極めて単純な事しか書いていないんだけどな・・・
問題あるプログラムだと書いてあるのも関わらず、解決するのが矛盾しているとか言われると、読んでいないとしか思えません。
予め忠告しておきますが、あまりにも記事とコメントを読まないのであれば、コメントを拒否しますよ。
コメントと記事を読まないのであれば、会話は不可能だと思います。
まだ分かってもらえないようなので、超簡単に書きます。
一、変数はなるべく狭い範囲に限定するべし。
一、ロックは最後の手段。
一、オブジェクト指向を学ぼう
一、並列処理も学ぼう
これでどうだろうか?
これでも分からないと言われたら正直言って無理です。
国語とプログラミングの基礎から学ぶ事をお勧めします。
>うーん。どこが分からないのだろう?
>正直言って、何が分からなくてそんな事を言っているのかが分かりません。
> (snip)
う〜ん...本文やコメントを何度読み返しても分からないからコメント書いてるんですけどね...まぁいいや。
とりあえず何が分からないかについて最初から説明させてもらいます。
長文ですがご容赦ください。
まず最初に提示されたサンプルを変数のアクセス範囲を分かるように変更します。
例えばこんな感じ。
----
bool flag;
void SampleFunc()
{
flag = true; //※flag変数は外部から変えられると仮定する
//← ここで別スレッドが割り込んでflag変えちゃうかも?
if ( flag == true ) {
//ここに来るよね?
} else {
//ここに来る事はないはずだけど・・・
}
}
----
で、この並列処理では期待通りに動作しないサンプルに対し、正しく動作させる為には
1)ローカル変数にする
2)ロックを掛ける
のどちらかが必要である、という答えが本文で提示されています。
更に 2 種類の答えを併記したのは flag が外部から操作されるのが想定外の動作である時と、正しい動作である時をどちらも想定しているからだ、とコメントを頂きました。
それはすなわち
1)ローカル変数にする ← flag が外部から操作されるのが想定外の時
2)ロックを掛ける ← flag が外部から操作されるのが正しい動作である時
という事だろうと考えます。
これらを踏まえた上で、先のサンプルをそれぞれ提示された回答で修正します。
多分こんな感じでしょうか。
回答1)ローカル変数にする場合
----
//bool flag;
void SampleFunc()
{
bool flag;
flag = true; //※flag変数は外部から変えられると仮定する
// ↑ ローカル変数にしたから外部から変えられなくなって安心♪
if ( flag == true ) {
//ここに来るよ
} else {
//ここに来る事はない
}
}
----
回答2)ロックを掛ける場合
# ロック範囲が広すぎる件はスルーの方向で。
----
bool flag; //flag変数は外部変数のまま
void SampleFunc()
{
thread_lock(); //ここでロック。ただし関数名は適当
flag = true; //※flag変数は外部から変えられると仮定する
// ←ロック掛けたからここで割り込まれる事がなくなって安心♪
if ( flag == true ) {
//ここに来るよ
} else {
//ここに来る事はない
}
thread_unlock(); //ここでロック解除。ただし関数名は適当
}
----
# 以下は回答2)で想定したロックを掛ける場合のコードが正しいという前提。
これらはどちらも本文中で提示された答えを踏まえて修正したコード、すなわちどちらも「並列処理が動作する」コードである筈です。
しかしながら回答2)をみると、flag は外部からアクセス可能なままですよね。
故にこのケースではコメントの中にある
>大概flagの様なステータス系変数が外部からアクセスされる時は、並列処理は動作しません。
を額面どおりに受け取った場合、ロックを掛けているにも関わらず「並列処理は動作しない」事になります。
しかし本文は元よりコメントでも「ロックは最終手段」とあるように、並列処理においてロックが有用である事は度々言及されています。
今現在もっとも分からないのはこの部分なのです。
要するに一エントリの中で
本文 :ロックを掛ければ並列処理は動作する
コメント:ロックを掛けても並列処理は動作しない
と書かれているように読めてしまうのだけれど、そんな矛盾はありえないから、きっとこちらが何かを誤解しているか、もしかしたら何か必要な言葉が書かれていないか、という状態なのでしょう。
ただそれを確認したいだけなんです。
オブジェクト指向的に正しくないといけない、とか、変数のアクセス範囲を狭めればいい とか、そんな次元の話は始めからしていないんですよ。
もちろん「回答2)ロックを使用する場合」で私が想定したコードは、貴方の想定しているコードとは違うのかもしれません。
そうであるなら「変数 flag は外部からアクセスされる仕様である」という前提で貴方が想定しているコードを提示していただけないでしょうか。
そのコードを元にして全体を最初から考え直したいと思います。
> しかし本文は元よりコメントでも「ロックは最終手段」とあるように、並列処理においてロックが有用である事は度々言及されています。
> 今現在もっとも分からないのはこの部分なのです。
>
> 要するに一エントリの中で
>
> 本文 :ロックを掛ければ並列処理は動作する
> コメント:ロックを掛けても並列処理は動作しない
>
> と書かれているように読めてしまうのだけれど、そんな矛盾はありえないから、きっとこちらが何かを誤解しているか、もしかしたら何か必要な言葉が書かれていないか、という状態なのでしょう。
> ただそれを確認したいだけなんです。
正しくロックをかけた場合、動作するのに決まっています。
そうでないならば、問題解決法として提示しません。
ようは変数で処理を切り替える状態はおかしいので、かなりの確率でオブジェクト指向設計的に間違っているという点です。
設計を見直し、それでも正しいのならばロックを掛けるしかありません。
if( 変数をローカルに出来るか? ) {
//リファクタリング開始
//ローカルに変更
//ロックを掛ける必要はない
} else {
//ロックを掛けよう
}
> オブジェクト指向的に正しくないといけない、とか、変数のアクセス範囲を狭めればいい とか、そんな次元の話は始めからしていないんですよ。
>
最初からそんな話ししかしていませんが・・・
だから何故分からないのかと不思議に思っていました。
問題を提示して、その解決法を書いているにも関わらず、解決すること自体が矛盾しているとか意味不明な事を言われて困っております。
※あるまじろさんはいつもこんな調子です。何時も一部の文を切り抜いて曲解します。
ですからこちらとしては、荒らしか本当に文章が読めないのかのうちどちらかしかないと考えております。
とにかく、この基礎を分からなければ、並列処理は理解できません。
私が言っているのは、初めに変数の範囲をよく考えて、それでも駄目ならばロックしかないと言う事です。
>
> もちろん「回答2)ロックを使用する場合」で私が想定したコードは、貴方の想定しているコードとは違うのかもしれません。
> そうであるなら「変数 flag は外部からアクセスされる仕様である」という前提で貴方が想定しているコードを提示していただけないでしょうか。
> そのコードを元にして全体を最初から考え直したいと思います。
大丈夫です私の頭の中のコードと一致する必要はありません。
ただ単に、変数の範囲を考えることと最悪の場合ロックを掛ける事しか言っておりません。
ロックはデッドロックなどのバグを生みますし、パフォーマンスを低下させますので、慎重にせねばなりません。
そもそも、コードが云々というレベルではありません。
実務では数十万行になるわけですから、この単純な事が如何に問題になるか分かりますよね。
>正しくロックをかけた場合、動作するのに決まっています。
>そうでないならば、問題解決法として提示しません。
...つまり私が理解できなかった下記の文は
>大概flagの様なステータス系変数が外部からアクセスされる時は、並列処理は動作しません。
↓
>大概flagの様なステータス系変数が外部からアクセスされる時は、“ロックを掛けないと”並列処理は動作しません。
と言う解釈でよいという事ですね。
それぞれの解決策がどういうケースを想定しているかを明言した直後に、何の前置きもなく解決策を講じていないケースに言及すると思ってなかったので、意味が理解できませんでした。
>> オブジェクト指向的に正しくないといけない、とか、変数のアクセス範囲を狭めればいい とか、そんな次元の話は始めからしていないんですよ。
>最初からそんな話ししかしていませんが・・・
>だから何故分からないのかと不思議に思っていました。
>問題を提示して、その解決法を書いているにも関わらず、解決すること自体が矛盾しているとか意味不明な事を言われて困っております。
...どうも、貴方には並列処理について書く際に
・オブジェクト指向と絡めないと並列処理は成立しない。
・オブジェクト指向の中に変数のアクセス範囲の検討がある。
という (貴方にとっては余りにも当たり前すぎて書かれていない) 前提があるようですね。
その前提を余りにも絶対視するが故に、並列処理だけでなくオブジェクト指向的にもダメなサンプルを「並列処理が動かない例」として出しても平気という処でしょうか。
しかしながら、ここは並列処理に言及しているエントリなので、並列処理以外にも原因があるケースを想定して問題を見る読者はほぼいないでしょう。
たとえ並列処理がオブジェクト指向の上に成り立っているとしても、です。
今回のケースなら、問題解決後も変数のアクセス範囲が変わらない事を大半の読者は想定します。
それが変わってしまう答えがいきなり提示されるので、あるまじろさんのような疑問を持つ人が出てくるのです。
蛇足ながら私も最初はあるまじろさんと同じ疑問を持っていました。
並列処理で問題を起こすサンプルを提示するなら、オブジェクト指向的には正しいが、並列処理をすると問題を起こすサンプルにして欲しいです。
もしオブジェクト指向的にも間違っている例を使わざるを得ないなら、その際は答えを同列に扱って欲くないですね。
「並列処理だけで考えればロック掛けるしかないけど、もし flag が外部から変更される必要がないなら flag をローカル変数にして外部からアクセスできなくするのが本筋」
とか、書きようは幾らでもあると思いますので。
>※あるまじろさんはいつもこんな調子です。何時も一部の文を切り抜いて曲解します。
>ですからこちらとしては、荒らしか本当に文章が読めないのかのうちどちらかしかないと考えております。
別に曲解してるのでも、荒らしでも、文章が読めないのでもなく、多分あるまじろさんが文章を読んだ時の前提が、貴方が読者に求めている前提と違うだけでしょう。
前提が違う解釈なので曲解に見えてしまうのでしょうが、基本ただの説明不足です。
...曲解っていうのはね、例えば「中の人の徒然草329」を読んで
「会話が不可能だとか、国語を学ぶ事を勧めるとか書いた直後にこんな事 (読解力の部分) 書いたエントリ挙げやがって、俺 (Geo=TK3) へのあてつけだな!」
と私が怒り出すケースの事を言うんですよ :p 。
>私が言っているのは、初めに変数の範囲をよく考えて、それでも駄目ならばロックしかないと言う事です。
ここには同意します。
> >正しくロックをかけた場合、動作するのに決まっています。
> >そうでないならば、問題解決法として提示しません。
>
> ...つまり私が理解できなかった下記の文は
>
> >大概flagの様なステータス系変数が外部からアクセスされる時は、並列処理は動作しません。
> ↓
> >大概flagの様なステータス系変数が外部からアクセスされる時は、“ロックを掛けないと”並列処理は動作しません。
>
> と言う解釈でよいという事ですね。
> それぞれの解決策がどういうケースを想定しているかを明言した直後に、何の前置きもなく解決策を講じていないケースに言及すると思ってなかったので、意味が理解できませんでした。
>
>
> >> オブジェクト指向的に正しくないといけない、とか、変数のアクセス範囲を狭めればいい とか、そんな次元の話は始めからしていないんですよ。
> >最初からそんな話ししかしていませんが・・・
> >だから何故分からないのかと不思議に思っていました。
> >問題を提示して、その解決法を書いているにも関わらず、解決すること自体が矛盾しているとか意味不明な事を言われて困っております。
>
> ...どうも、貴方には並列処理について書く際に
>
> ・オブジェクト指向と絡めないと並列処理は成立しない。
> ・オブジェクト指向の中に変数のアクセス範囲の検討がある。
>
> という (貴方にとっては余りにも当たり前すぎて書かれていない) 前提があるようですね。
> その前提を余りにも絶対視するが故に、並列処理だけでなくオブジェクト指向的にもダメなサンプルを「並列処理が動かない例」として出しても平気という処でしょうか。
>
「さて、先ほどのプログラムの問題を解決するにはどうすればいいと思いますか?
その答えは、「flagをローカル変数にする」か「ロックを掛ける」です。
このうちどちらが望ましいかといいますと、ロックはパフォーマンスを低下させるので、ローカル変数にする方です。」
とちゃんと書いております。
どちらが望ましいのかと書いているのですから、「flagをローカル変数にする」か「ロックを掛ける」であり、両方が解決法としてふさわしいわけです。
どちらか一方を書くと、「並列処理って、ロックを書けなくてもローカル変数にすれば大丈夫なんだな。あれ?でもローカル変数に出来ないや・・・ならば並列処理は無理かな。」と読者に誤解される恐れがあります。
ローカル変数しか書いていない場合は、「ローカル変数に出来なければ並列処理は出来ない」事になってしまいます。
またロックしか言わない場合、デッドロックなどのバグを生む恐れがありますので、読者を危ない方向へ導く事になります。
落ち着いてちゃんと読んでほしいな。
文章は全ての文に意味があり、その並びも設計されて書かれているのですから、ちゃんと一文一文を読まないと文章は理解できません。
私にとって文章もまたプログラミングです。
分析・設計・実装の手順で書いております。
> しかしながら、ここは並列処理に言及しているエントリなので、並列処理以外にも原因があるケースを想定して問題を見る読者はほぼいないでしょう。
> たとえ並列処理がオブジェクト指向の上に成り立っているとしても、です。
>
> 今回のケースなら、問題解決後も変数のアクセス範囲が変わらない事を大半の読者は想定します。
> それが変わってしまう答えがいきなり提示されるので、あるまじろさんのような疑問を持つ人が出てくるのです。
> 蛇足ながら私も最初はあるまじろさんと同じ疑問を持っていました。
>
> 並列処理で問題を起こすサンプルを提示するなら、オブジェクト指向的には正しいが、並列処理をすると問題を起こすサンプルにして欲しいです。
> もしオブジェクト指向的にも間違っている例を使わざるを得ないなら、その際は答えを同列に扱って欲くないですね。
情報処理技術はつながっているから学習するべきだというテーマも書いております。
それに、スレッドセーフの定義が「並列的に処理しても仕様通り動く事」(極力簡潔にしています)ですから、シングルスレッドでも「なんだかなー」のプログラムは、並列処理でもやはり駄目なわけです。
オブジェクト指向言語で実装する時、ちゃんとオブジェクト指向しないと問題外です。
これは非常に重要な事であり、書かないわけにはいきません。
ちなみに、関数型言語で並列処理する場合も、「設計が駄目ならば並列処理も駄目」です。
シングルスレッドでも問題があるプログラムは並列処理をしても問題です。
>「さて、先ほどのプログラムの問題を解決するにはどうすればいいと思いますか?
>その答えは、「flagをローカル変数にする」か「ロックを掛ける」です。
>このうちどちらが望ましいかといいますと、ロックはパフォーマンスを低下させるので、ローカル変数にする方です。」
>とちゃんと書いております。
その文章があるからこそ
・オブジェクト指向と絡めないと並列処理は成立しない。
・オブジェクト指向の中に変数のアクセス範囲の検討がある。
という書かれていない前提があり、更に読者も同じ前提を持っているとするほどに絶対視しているから
・オブジェクト指向的にもダメなサンプルを並列処理が動かない例として出しても平気
なのでは?と思っています。
そもそもその両者が解決法としてふさわしくなるのは flag変数のアクセス範囲が広いからです。
しかし前者の解決法「flagをローカル変数にする」=「変数のアクセス範囲を検討する」は、並列処理に特有の解決法ではありません。
本文中にも
>ですが、オブジェクト指向では、口うるさく「変数を外部に出すな」とカプセル化の原則を言われます。
とあるように「flagをローカル変数にする」事はオブジェクト指向的な解決法であり、つまり並列処理とは関係なく検討すべき内容です。
それはすなわちオブジェクト指向的に正しいプログラムにおいて「flagをローカル変数にする」という解決法は有り得ないという事を意味します。
オブジェクト指向的に正しいプログラムであれば必ず並列処理は動作するというなら別ですが、現実にはそうでない以上、並列処理に特有といえる解決方法はロックしか有りません。
にも関わらずロックについての説明がほとんどなく、変数のアクセス範囲ばかりに言及しているから、前述のような前提などがあるのではないか?と思わざるを得ないのです。
解決策としてロックしか提示しなかった場合にデッドロックなどのバグを発生させる危ない方向に誘導する危険性を懸念されていますが、むしろそういう点にまできちんと触れておく方が並列処理のエントリとしてはよかったのではないでしょうか。
# その点に関しては私も過去のコメントで言及しておくべきでした。
# 謹んで謝罪いたします。
>落ち着いてちゃんと読んでほしいな。
>文章は全ての文に意味があり、その並びも設計されて書かれているのですから、ちゃんと一文一文を読まないと文章は理解できません。
すみません、どうにか理解しようとして連休一日潰して何度も読み返しましたが、結局ムリでした。
やっぱり前提条件も明記しないままに複数の解釈が成立する文章を何度読み返した処で、何が言いたいのかなんて理解出来ません。
だからコメント欄で、もしかしてこういう前提条件をもっているのか?とか、どういう意図で書いた文章なの?とか聞いてるんですよ。
まぁそれだけでなくて異議なり意見なり要望なりも書いてますが。
>情報処理技術はつながっているから学習するべきだというテーマも書いております。
>それに、スレッドセーフの定義が「並列的に処理しても仕様通り動く事」(極力簡潔にしています)ですから、シングルスレッドでも「なんだかなー」のプログラムは、並列処理でもやはり駄目なわけです。
>オブジェクト指向言語で実装する時、ちゃんとオブジェクト指向しないと問題外です。
>これは非常に重要な事であり、書かないわけにはいきません。
>ちなみに、関数型言語で並列処理する場合も、「設計が駄目ならば並列処理も駄目」です。
>シングルスレッドでも問題があるプログラムは並列処理をしても問題です。
つながっているから学習するべき、はいいんですが、だからといってオブジェクト指向と並列処理をまとめて扱わなければならない事にはなりません。
また並列処理には並列処理であるが故に気をつけなければならない事がある筈で、並列処理メインのエントリなら、オブジェクト指向で重要な事よりも並列処理特有の事情の方がよっぽど「書かない訳にはいかない」事でしょ。
更に (スレッドセーフの定義の是非などはおいておくとして) シングルスレッドで問題があるプログラムが並列処理でもダメなのは当たり前の話です。
そんなプログラムを何の前置きもなく「並列処理で問題の起こるコード」として提示する事自体どうかと思いますが、挙句にシングルスレッドで問題なく動くようにするだけの事を「並列処理を動作させる為の解決法」とするのは愚の骨頂でしょう。
サンプルで妥当な解決策に見えるのは、複数スレッド間でデータのやり取りがない仕様を想定した為にシングルスレッドで正しく動くようにすればよかっただけの話に過ぎませんよ。
繰り返しになりますが並列処理のエントリなら「シングルスレッドでは問題のないプログラムが、並列処理では『なんだかなー』になってしまう例」をサンプルとして提示すべきだし、そうできないなら「並列処理に絡む部分だけを問題にすべき」です。
なんというか、無理やり感がただよっていますね。
余りにも苦しいコメントです。
自分が読み間違った事を認めてはいかがでしょうか?
例えば、
>その文章があるからこそ
>・オブジェクト指向と絡めないと並列処理は成立しない。
>・オブジェクト指向の中に変数のアクセス範囲の検討がある。
>という書かれていない前提があり、更に読者も同じ前提を持っているとするほどに絶対視して
>いるから
>・オブジェクト指向的にもダメなサンプルを並列処理が動かない例として出しても平気
>なのでは?と思っています。
これ自体が凄い無理やりです。
何度も言っているように、並列処理単体で考えるなんて事あり得ません。
オブジェクト指向言語でプログラミングをして、「並列処理をしたいんだからオブジェクト指向は無視したぜ」なんて言い訳は通用しません。
それと、私が書いている文章をちゃんと読んでいないと指摘しています。
前提ではなくて【書いているのです】
それが本当に分からないのであれば、読解力がなさすぎます。
この記事の最後の方に、情報処理技術はつながっていると明記しております。
こういったところが、無理やり難癖をつけているか、本当に読解能力がないのかしか考えられないゆえんです。
しかもあなたは、
>しかし前者の解決法「flagをローカル変数にする」=「変数のアクセス範囲を検討する」は、
>並列処理に特有の解決法ではありません。
と書いておりますよね。
従って、どちらかというと難癖ですね。
自身でも変数を考えるのは当たり前だと明言しておきながら、並列処理と同時に考えられないといのは矛盾しております。
さらに貴方は私の記事を無視して
>それはすなわちオブジェクト指向的に正しいプログラムにおいて「flagをローカル変数にする」という解決法は有り得ないという事を意味します。
と話しを勝手に進めていますね。
だから、何度も言いますが、私はオブジェクト指向と並列処理の接点を書いているのです。
その接点を書くのが目的なのに、そういった事を書くはずがありません。
難癖をつけていると感じましたが、どうやら貴方の場合は両方なようですね。
読解能力がないうえに難癖をつけてくる。非常に迷惑です。
あと、
「
つながっているから学習するべき、はいいんですが、だからといってオブジェクト指向と並列処理をまとめて扱わなければならない事にはなりません。
また並列処理には並列処理であるが故に気をつけなければならない事がある筈で、並列処理メインのエントリなら、オブジェクト指向で重要な事よりも並列処理特有の事情の方がよっぽど「書かない訳にはいかない」事でしょ。
更に (スレッドセーフの定義の是非などはおいておくとして) シングルスレッドで問題があるプログラムが並列処理でもダメなのは当たり前の話です。
そんなプログラムを何の前置きもなく「並列処理で問題の起こるコード」として提示する事自体どうかと思いますが、挙句にシングルスレッドで問題なく動くようにするだけの事を「並列処理を動作させる為の解決法」とするのは愚の骨頂でしょう。
サンプルで妥当な解決策に見えるのは、複数スレッド間でデータのやり取りがない仕様を想定した為にシングルスレッドで正しく動くようにすればよかっただけの話に過ぎませんよ。
繰り返しになりますが並列処理のエントリなら「シングルスレッドでは問題のないプログラムが、並列処理では『なんだかなー』になってしまう例」をサンプルとして提示すべきだし、そうできないなら「並列処理に絡む部分だけを問題にすべき」です。
」
これらは貴方の意見を押し付けているだけです。
私は並列処理を学習する前にオブジェクト指向を学習するべきだと考えております。
貴方が並列処理だけを学習すればいいと考えているのであれば、貴方自身が自分のブログに書けばいいだけの話しです。
貴方は自分の意見と、私が書こうとしている記事の内容を混同しております。
何度も言っておりますが、スレッドセーフの定義から考えて、オブジェクト指向として正しい事は前提です。
その前提を経ずに並列処理だけ考えても問題外なのです。
逐次処理でも問題があるものは、並列処理でも問題があるのです。
それは並列処理の基礎中の基礎であり書く必要があります。
スレッドセーフの定義を満たしていないものは問題です。
そして、スレッドセーフでないプログラムは並列処理で正常に動作しません。
その基礎すらも書いていない方が問題かと思います。
それに加えて、この部分でも文章を読めていませんね・・・
総論しますと、貴方は自分の意見だけで物事を捉え、人の記事の内容を読もうともせず、自分が考える内容と違うと難癖をつけているだけです。
※おまけに読解力に問題があるようです
貴方が並列処理について書きたいのであれば、自分のブログですればいいのです。
人のブログに来て、自分の意見と違うからと言って難癖を書くのは非常に迷惑な行為です。
自分が知りもしない事で難癖をつけて、おまけに一人で言えないから、他の人を煽って集団でわめき散らかす人よりもましですが、十分迷惑な行為なので止めてください。
悪い事はいいません。
貴方の場合、専門書をもっと読んで読解力を身につける所から始めるべきです。
>そんなプログラムを何の前置きもなく「並列処理で問題の起こるコード」として提示する事自体どうかと思いますが、挙句にシングルスレッドで問題なく動くようにするだけの事を「並列処理を動作させる為の解決法」とするのは愚の骨頂でしょう。
あと、どうやら貴方は根本的に理解されていないようですが、このコードは「並列的にも問題があるもの」です。
基礎中の基礎で躓いているのが感じ取れます。
貴方が並列処理に関して知らない事はよくわかりました。
知らない事を無理やりコメントするのはよした方がよろしいかと思います。
知らない事を無理に書こうとするから難癖にしかならないのでしょう。
どうやら、知らないのにもかかわらず、無理やり文章を曲解して難癖をつける事がはやっているようですが、そんな恥ずかしい事は止めておいた方が貴方の為です。
恥ずかしい流行りを真似しても何の意味もありません。
貴方が恥ずかしくなるだけです。
>というか、未定義ではなくて、コンパイルエラーが出るよ。
「未定義」になり「コンパイルエラー」が出るね。
で、この場合の解決策は?
> >というか、未定義ではなくて、コンパイルエラーが出るよ。
>
> 「未定義」になり「コンパイルエラー」が出るね。
>
> で、この場合の解決策は?
未定義ではなくて、アクセスするコードから見えなくなるからコンパイルエラーです。
リファクタリングを知らないのですね・・・
だから、知りもしないのに、無理やり間違っていると言おうとするから、難癖にしかならないと言っているのですが・・・
何を言っても無駄そうですからこれからコメントを拒否します。
>何を言っても無駄そうですからこれからコメントを拒否します
それは答えられないからコメントを拒否すると言うことですね。
原因は何であれ、コンパイルエラーは出る。
ただ、その時の解決方法が解らないんだよね。
これは凄い。余りにも酷いので掲載する事にしました。
アクセス範囲を変えて、エラーが出たら対処できないとは!
どうやらプログラミングを全く知らないのですね・・・
おまけに私のコメントも読んでいないようですね。
日本語の読解力を高めるための読書と、プログラミングの基礎から学ぶ事をお勧めします。
今ふと思ったのですが、あるまじろ氏はプログラミングを全く知らないようなので、もしかして私の質問したかったのかな?
そうだとしたら質問の仕方も考えた方がいいです。
人に質問する時は、間違っているとか矛盾しているとか言ったら駄目です。
分からないからその人に聞くのであって、
教えてもらうからにはマナーをわきまえる必要があります。
そもそも、自分が分からないのは他人のせいだと考えたら駄目です。
ひとまず質問の仕方を職場の先輩に教えてもらおう。
>アクセス範囲を変えて、エラーが出たら対処できないとは!
これだけの条件じゃ自分には対応できないね。
外部参照していた flag が見えなくなるわけだから。
どうせ回答も出来ないのだろうけど。
変に回答するとボロがでるしね。
誰もが見て納得できる回答を示すことができる?
>ただ、その時の解決方法が解らないんだよね。
これはインドリさんに対する問いかけだったんだけどな。
まあ、自分でも判らんから教えてくださいよ。
> >アクセス範囲を変えて、エラーが出たら対処できないとは!
>
> これだけの条件じゃ自分には対応できないね。
> 外部参照していた flag が見えなくなるわけだから。
>
> どうせ回答も出来ないのだろうけど。
> 変に回答するとボロがでるしね。
>
> 誰もが見て納得できる回答を示すことができる?
>
> >ただ、その時の解決方法が解らないんだよね。
>
> これはインドリさんに対する問いかけだったんだけどな。
>
> まあ、自分でも判らんから教えてくださいよ。
知りもしないのに、言っているという事がよくわかりますね。
どうしてそんな恥ずかしい事が出来るのかと不思議でなりません。
それを知らないのは貴方ぐらいのものです。
リファクタリングを知らなくても分かる、基本的なプログラミングです。
誰でも知っていると思いますが、教えて欲しいようだから一応書きます。
・当然のことながらヴァージョン管理ソフトに今のコードをコミットする。
・グローバル変数を取り除く。
・ローカル変数にする。
・エラー箇所を吟味する。
・その変数の必要がないように、オブジェクト指向設計を変える。
もしくは
・変数を変更するメソッドまたはプロパティを用意してロックを掛ける。
プログラミングの基礎も知らずに、人に挑戦的な事を書くのはよした方がよいですよ。
その態度じゃ、普通教えてもらえません。
それにしても、E氏と似ているな・・・
自分の過ちを認められずにまた難癖をつけてくるだろうから先に言っておきます。
当然のことながら「ソースコードを変える権限がない時は対処のしようがありません」
変更できないのですから、デバッグもままなりません。
デバッグが出来ないのであれば、当然エラーも修正できません。
これが分からないのであれば、貴方にかまっている暇はないので無視します。
実際問題、「日本語が読めない」「プログラミングの基礎も知らない」「過ちを認めない」
の三拍子をされたら会話自体が不可能ですし、そんな態度の人に教える義理もありませんしね。
私はテクニカルアドバイザーもしているので、商売として情報を提供している身です。
それを、こんな変な人にただでサービスを提供する必要はありません。
おっと、忘れていた。
相変わらず人の話しを聞いていないようなのでもう一度言います。
プログラミングの基礎も知らないようですから、恐らく若い人なのでしょう。
「人にものを教えてもらう態度を先輩に教えてもらいましょう」
貴方の場合、コメントする以前の問題ですから、先輩に社会人としての基礎を教えてもらってから来てください。
> 「さて、先ほどのプログラムの問題を解決するにはどうすればいいと思いますか?
> その答えは、「flagをローカル変数にする」か「ロックを掛ける」です。
> このうちどちらが望ましいかといいますと、ロックはパフォーマンスを低下させるので、ローカル変数にする方です。」
> とちゃんと書いております。
> どちらが望ましいのかと書いているのですから、「flagをローカル変数にする」か「ロックを掛ける」であり、両方が解決法としてふさわしいわけです。
元のプログラムには、「flag = true; //※flag変数は外部から変えられると仮定する」と前提がなされています。「flagをローカル変数にする」と、外部から変えることができなくなるため、前提が崩れてしまいます。普通は、この前提を覆さずに、問題を解決します。前提を覆さなければ解決できない場合は、覆す理由を説明します。
もし、「うるさく「変数を外部に出すな」とカプセル化の原則を言われます。」というのが、前提を覆すことの説明であるなら、言葉が足りなすぎます。この場合、組むところまでいってしまったプログラムを、設計からやり直さなければなりません。カプセル化の原則は「問題を起こさないための方法」であって、「問題を解決するための方法」ではありません。もっとも普通は、原則があって、原則を破るから「外部から変えられると仮定する」と、断りを入れるのですけどね。
たくさんの本を読んでいると公言するわりに、説明の仕方は下手ですね。もっと、「どの様に文が構成されているか」ということにも気をつけて、本を読んでください。
「自分が知っていることは他人も知っていると思っている」という自覚があるなら、「自分は知っているこのことは、他人も知っているだろうか」と自問するクセを付けて下さい。「私ごときが知っているのだから」というのは、「私でさえ知っていることも知らないのか」という侮りでもあります。
これまでも何度も、「読み返せ」という指摘をされているはずです。「何も知らない人が読んで理解できるだろうか」という視点(初心者への説明であるならそういう視点が必要なはずです)で、読み返してから投稿して下さい。また、アドバイスを仕事にしているからこういう場では仕事に関わるようなことは書けないというのなら、一切書かないでください。つまり、ブログから撤退してください。中途半端な情報があることは、大変迷惑です。
貴方も相変わらず、人の話しを聞きませんね。
ちゃんと問題があると説明しております。
引用
「
このプログラムでも、false時の処理がされる場合があります。逐次処理に慣れきった人は、ここで足元をすくわれます。
ですが、複数の処理を並列的に行っているのですから、flagを変える処理が割り込めば、falseになる可能性があるのです。
こんな単純な処理ですら、逐次処理の感覚でしていれば罠にはまります。
」
問題のあるプログラムを提示した直後に書いてあるので、普通は気付きます。
貴方も文章を読まず、会話にずれが生じている事が多々ありますが、
(@ITの掲示板やコラムのコメント欄を参照)
あわてずにちゃんと人の話しを聞きましょう。
不自然なコメントは、何かを言ってやろうと無理やり言っているように聞こえます。
かなり印象が悪いので、冷静に人の話しを聞くべきです。
あと、知らない用語が書いていたら相手が可笑しいと考えて、「プロじゃない」などといった事がありましたよね。
自分が知らない事は間違っているという考えは止めた方がいいですよ。
そんなことではチームプレーはできません。
また曲解されたら嫌なので明言しておきますが、私は攻撃ではなくて忠告をしております。
人の忠告は素直に聞いた方がいいですよ。
最後に貴方は根本的な勘違いをしております。
私がこの人たちに注意をしているのは「人にものを尋ねる時の態度」です。
貴方は聞く側は何をしてもいいという持論を持っているようですが、世間一般では通用しません。
会話は両者が礼儀をわきまえてするものです。
特にここは公共の場です。
人様に失礼がないようにしましょう。
この問題のプログラム、注釈を書かないとローカル変数にも見えます。
だからこれはグローバル変数であり、外部からアクセスされていると一々書いているのです。
恐らく難癖を如何にしてつけようか考えているから、この様な普通の事も気付かないのだと思いますが、それでは自分は日本語が読めないと宣伝しているのと同じです。
何故、こんな恥ずかしい事が出来るのか到底分りませんが、もういい加減に自分の能力のなさをアピールして、人様に迷惑をかけるのはよしましょう。