2

SOや他のサイトで他の質問が「並行コードを書くにはどうすればよいですか? 」と尋ねる場合、答えには常に、コード内の「データの依存関係の確認」や「相互依存関係」などのかなり漠然とした説明が含まれます。これらの不思議な依存関係が実際にJavaコードとしてどのように見えるのか疑問に思います!?!

  • ある部分が別の部分に依存していないために簡単に並列化できるコードのセクションの具体的な例は何ですか?
  • これらの依存関係が存在するためにシリアルでなければならないコードのセクションの具体的な例は何ですか?
  • これらの依存関係の存在は、スレッドプールを使用するかどうかの決定にどのように影響しますか?

ここでは「木々の間の森」が見えていないのではないでしょうか。前もって感謝します!

4

4 に答える 4

3

ここにいくつかの簡単な例があります:

1.依存関係がなく、並列化が簡単:

 int[] array = new int[size];
 for(int i = 0; i < size; i++) {
     array[i] = array[i] * array[i];
 }

各項目は個別に計算されます。

2.前の反復への依存:

 int[] array = new int[size];
 for(int i = 1; i < size; i++) {
     array[i] = array[i - 1] * 2 + array[i];
 }

array[3]計算するまでは計算できませんarray[2](場合によっては、リストラを可能にする式を推測できますが、アイデアは得られます)。

スレッドプールの使用は、依存関係の有無に実際には関係していません。これらの概念は直交しています。スレッドプールは、システム内で一般的に高価なリソースであるスレッドをリサイクルするための効率的な方法です。

于 2012-04-28T17:00:45.603 に答える
1

銀行口座オブジェクトを作成し、不変条件を維持したいとします。銀行口座の操作の結果として、お金が魔法のように作成または破棄されることはありません。

あなたは書くかもしれません

int balance;

void deposit(int amount) {
  if (amount < 0) { throw ... }
  long after = balance + amount;
  if (after > Integer.MAX_VALUE) { throw ... }
  // DANGER 1
  balance = (int) after;
}

void withdraw(int amount) {
  if (amount < 0 || amount > balance) { throw ... }
  // DANGER 2
  balance -= amount;
}

1つのスレッドが危険コメントのいずれかで一時停止し、同時に別の入金または出金が発生した場合に何が起こるかを考えてみてください。

ある預金が危険1で開始および一時停止し、次に別の預金が発生し、その後最初の預金が終了して価値が失われると、預金が失われる可能性があります。

1回の引き出しが開始され、危険2で一時停止し、その後預金が発生してから引き出しが終了した場合、引き出しによって残高がマイナスになる可能性があります。

入金が開始され、危険1で一時停止し、引き出しが行われ、入金が終了すると、お金が発生する可能性があります。


これを解決する標準的な方法は、値を計算してafter割り当てるデポジットのコードbalance = (int) afterがクリティカルセクションであり、依存関係に介入する変更なしで発生する必要があると言うことですbalance

同様に、安全チェックインwithdrawal-=操作はクリティカルセクションを形成します--=チェックがまだ真でない場合、操作は発生しないはずです。

a -= bボーナスの楽しみのために、実際にはいくつかのステートメントが内部にあるため、何が起こるかを考えてください。

int x = b;
int y = a;
a = y - x;

意味のあるものにするためには、これらすべてが一緒に発生する必要があります。したがってa -= b、標準的な意味を持つためには、その依存関係に依存し、操作の途中で変更されませんab

于 2012-04-28T17:01:38.013 に答える
1

ある部分が別の部分に依存していないために簡単に並列化できるコードのセクションの具体的な例は何ですか?

並行アプリケーションは、の実行に基づいて構築されますtaskstaskを任意の個別の作業単位として識別できます。個別の作業単位として際立っており、明示的なタスク境界Runnableを持つコードの部分を識別できる場合は、タスクごとにを作成し、個別に実行Threadして並列処理を実行できます。実際、aは抽象化をRunnable表します。 例として、処理のために複数のファイルをロードする必要があるプロセスがあります。ファイルのロードは個別の作業単位であり、コードフローをブロックすることなくスレッドによってバックグラウンドで実行できます Task

これらの依存関係が存在するためにシリアルでなければならないコードのセクションの具体的な例は何ですか?

Task 2の計算/処理に依存する場合は、完了するTask 1まで待機するTask 2必要があります。これにより、シーケンスがシリアル化されます。 例として、バックグラウンドでファイルをロードしてから、ファイル内のキー検索しますTask 1

これらの依存関係の存在は、スレッドプールを使用するかどうかの決定にどのように影響しますか?

彼らはしません。aの作成Threadはコストのかかる操作でありThread Pools、新しいスレッドを作成するオーバーヘッドを回避するためにスレッドを再利用するための構成設計です。Thread PoolあなたはsTasksとして渡すだけで、必要に応じてスレッドにを割り当てるか、新しいスレッドを作成する責任があります。スレッドプールなどを介してポリシーを定義することもできます。ただし、これらは並行プログラム用のツールです。実行のためにスレッドプールに送信するタスクをすでに特定している必要があります。RunnableThread PoolTask

于 2012-04-28T18:06:39.857 に答える
0

依存関係がない簡単な例は、コレクションを通過するループで、すべてのアイテムに対して同じ作業を実行します(FPの場合はマップ操作)。依存関係が存在する典型的な例は、多くのdbフェッチ、それらに基づく決定、さらにフェッチを行うビジネスロジックです。これらはすべてネストされたifで行われ、多くのデータ処理が行われますが、各ステップは前のステップの結果によって異なります。

于 2012-04-28T17:01:57.190 に答える