Javaまたはその他のプログラミング言語での構成可能な同時実行性


8

SoftwareとConcurrency Revolutionhtmlバージョン)という名前の並行性に関する研究論文を読んでいたとき。私は次の行に出くわしました:

残念ながら、ロックは機能しますが、最新のソフトウェア開発にとって深刻な問題を引き起こします。ロックの基本的な問題は、ロックが構成可能でないことです。正しいロックベースのコードを2つ取り、それらを組み合わせて、結果がまだ正しいことを知ることはできません。現代のソフトウェア開発は、ライブラリをより大きなプログラムに構成する機能に依存しているため、ロックベースのコンポーネントを、その実装を調査せずに構築することは困難です。

  1. 私は、Javaがどのように構成可能な同時実行性を保証するか、あるいはこのシナリオを生成する方法があるかさえ考えていました。

  2. また、1つ以上のライブラリのデータをどのように同期させることができますか?プログラマーは自分のプログラムからそれを行うことができますか、それとも物事を同期させるのはライブラリ次第です。

  3. Javaでない場合、ロックベースの同時実行を使用し、構成可能な同時実行を保証する他の言語はありますか?

以下も同じ論文から引用したものです。

同期メソッドには少なくとも3つの大きな問題があります。1つは、他のオブジェクト(Javaのベクターや.NETのSyncHashTableなど)の仮想関数を呼び出すメソッドを持つ型には適していません。ロックを保持したままサードパーティのコードを呼び出すと、デッドロックが発生する可能性があるためです。第2に、同期されたメソッドは、すべてのオブジェクトインスタンスのロックを取得および解放することにより、多くのロックを実行する可能性があります。第3に、プログラムがオブジェクトまたは異なるオブジェクトに対して複数のメソッドを呼び出すときに原子性を維持しないため、同期化されたメソッドはロックをほとんど実行できません。後者の簡単な例として、銀行振込を考えてみましょう。account1.Credit(amount); account2.Debit(amount)...

注:論文は2005年9月に発行されました



@ErikEidtの論文には94の引用があります

3
質問への回答ではありませんが、対話をメッセージパッシングに制限すると、より適切に動作する同時実行性が得られます。非同期メッセージキューを介して通信するプロセス(スレッドだけでなく、プロセス全体)。確かに、脅威ベースのモデルほどパフォーマンスは良くありませんが、メッセージの送信がブロックできない場合、メッセージに実際の循環データ依存関係がコード化されていない限り、デッドロックすることはできません。最も重要なのは、すべてのメッセージがアトミック操作として簡単に構成/解釈されるため、一貫した状態のすべての頭痛がなくなることです。
cmaster-モニカを2016年

1
ソフトウェアトランザクションメモリは、構成可能な同時実行構造です。HaskellにはSTMライブラリがあります。
comestorm

1
@ペンギン:並行性!=並列処理。Javascript(node.js)は、非常に高い同時実行性(実際にはすべてのI / Oが同時実行)を持つ言語の良い例ですが、シングルスレッドです。Javaには、Futures(CompletionStage)の形で、このためのかなり優れたフレームワークがあります。もちろん、コードがどのようにロックフリーになるかは実装に依存しますが、開発のためのツールがあります。
slebetman 2017

回答:


7

Java言語ではありません。ロック(ミューテックス)の性質です。

正しいことを保証しながら並行性を改善するためのより良い方法、言語に依存しない方法があります:

  1. 不変オブジェクトを使用して、ロックが不要になるようにします。
  2. promiseと継続渡しスタイルを使用します。
  3. ロックフリーのデータ構造を使用します。
  4. ソフトウェアトランザクションメモリを使用して状態を安全に共有する。

これらの手法はすべて、ロックを使用せずに同時実行性を向上させます。特にJava言語に依存するものはありません。


3

私は、Javaがどのように構成可能な同時実行性を保証するか、あるいはこのシナリオを生成する方法があるかさえ考えていました。

記事で述べているように、仮想メソッド(または関数をパラメーターとして渡すなどの他の同様のメカニズム)と共にロックを使用する場合、構成可能性を保証することはできません。コードの一部が別のコードからの仮想メソッドにアクセスでき、両方ともロックを使用する可能性がある場合、安全に(つまり、デッドロックのリスクなしに)2つのコードを構成するには、ソースコードを検査する必要があります。どちらも。

また、1つ以上のライブラリのデータをどのように同期させることができますか?プログラマーは自分のプログラムからそれを行うことができますか、それとも物事を同期させるのはライブラリ次第です。

一般に、ライブラリを使用して同期を行うのはプログラマーの責任です。このようにして、プログラマーはすべてのロックがどこにあるかを認識し、デッドロックが発生しないことを確認できます。

Javaでない場合、ロックベースの同時実行を使用し、構成可能な同時実行を保証する他の言語はありますか?

繰り返しますが、この記事の要点は、これは不可能であるということです。


この論文は2005年に発行されました。あなたの答えは同じ論文に基づいています。言語は2005年から大きく進化しています。最新の参考資料を教えたり、回答を再確認したりできます。

2
その論文は、2005年に固有のことについては何も述べていません。ロックベースのライブラリが構成可能ではないことは、2105年にも当てはまります。
2016年

Javaは常にロック構造を更新しているため、Java5からJava8までのJava同時実行パッケージを比較できます。

それはどうでもいい事です。Java 8は、ロックを使用する際のデッドロックの可能性を排除しなかったので、これについては何も変わっていません。
2016年

2
@penguin-構成できない機能がある言語を完全に構成可能にすることは不可能です。閉じたモジュールが危険な方法でそれらを使用していないことを保証できないためです。ロックを使用している場合でも(この場合は使用方法に注意する必要があります)、コードを構成可能にすることができますが、任意のライブラリやシステムコンポーネント、あるいはその両方がそうでないかどうかは確認できません。
ジュール、

1

低レベルのロックメカニズムは本質的に構成できません。これは主に、ロックが世界の下に達して、命令を実行するマシンに影響を与えるためです。

後続のJavaライブラリには、正しいマルチスレッド操作を保証するためのより高いレベルのメカニズムが追加されています。彼らはの使用を制約することによって、これを行うlock()volatile、特定のよく知られており、制御可能な状況に。たとえば、並行キューの実装には非常に局所的な動作があり、前の状態と後の状態について推論することができます。より高いレベルのメカニズムを使用することは、それを正しくするために、仕様やコードをあまり読む必要がないことを意味します。しかし、これは重要ですが、サブシステムのロックモデルと、サブシステムが相互にどのように相互作用するかを理解する必要があります。また、Java 5以降の同時実行性に関するJavaの変更は、ほとんど言語に関係なくライブラリにのみ関係しています。

ロックメカニズムの主な問題は、状態に影響し、時間領域で動作することです。人間もコンピュータも、状態や時間のどちらについても十分に推論しません。コンピューターの科学者がモナドを思いついたのは、言語の構成可能性に関して最初に思い浮かんだのは、価値と構造について推論する能力です。

私たちが近づいたのは、順次プロセスの伝達です。これには、メールボックスやメッセージパッシングなどの高度なメカニズムが必要です。私の控えめな意見では、CSPはまだ大規模なシステム(構成可能なソフトウェアの最終的なターゲット)または時間ベースの推論のいずれかを適切に処理していません。


1
これは私の質問に答えません。Javaが構成可能かどうか、または他の言語があるかどうか知りたいのですが。私が得た2つの回答によると、Javaは構成可能ではありませんが、どちらの回答も具体的な証拠を提供していません。

ロックのみを使用するJavaは構成可能ではないという証拠が必要でしたか?また、上記のCSPの記事を読むと、構成可能であると言及されている言語がたくさん表示されます。私のコメントは、任意の言語を参照するlock()volatile、スレッドやプロセス同期の粒度です。
BobDalgleish 2016年

さて、私は興味があるものがあります。あなたの答えであなたは「私たちが来た最も近い...」と言ったそれはどういう意味ですか?どのようにしてCSPが最も近いものであるとわかりましたか?

1

まず、この質問に回答してくれたすべてのメンバー、特に私の回答に非常に近いと思われるRobert Harveyに感謝します。

私は2年間並行処理の概念について研究してきましたが、私の調査結果によると、その並行処理構文が構成可能であることを保証する言語はありません。不変のデータ構造とSTMを使用した完全に優れた実行コードでも、内部ではSTMがロックを使用するため、予期しない結果が生じる可能性があります。STMはアトミック操作に非常に適していますが、モジュール間の同時実行性の比較の構成可能性について話す場合、STMが期待どおりに動作しない可能性があります(非常にわずか)。

しかし、それでも、以下を使用することで不確実性を最小限に抑えることができます(技術/方法/構成):

  1. ロックと同期の回避
  2. STMの使用
  3. 永続的(不変)データ構造の使用
  4. 状態の共有を避ける
  5. 純粋な機能
  6. 非同期プログラミングスタイル
  7. 頻繁なコンテキスト切り替えの回避
  8. マルチスレッドのパラダイムとその環境の性質が大きな役割を果たす

おそらく最も根本的な異論[...]は、ロックベースのプログラムが構成しないことです。正しいフラグメントを組み合わせると失敗する可能性があります。—Tim Harris et al。、 "Composable Memory Transactions"、Section 2:Background、pg.2

更新

ジュールのおかげで、私は正直に立っています。STMはさまざまな実装を使用しており、それらのほとんどはロックフリーです。しかし、STMはここでは良い解決策であると私はまだ信じていますが、完璧な解決策ではなく、欠点もあります。

トランザクション内からのメモリ位置の各読み取り(または書き込み)は、データの読み取り(または書き込み)のためのSTMルーチンの呼び出しによって実行されます。順次コードでは、これらのアクセスは単一のCPU命令によって実行されます。STMの読み取りおよび書き込みルーチンは、通常、すべてのアクセスに関する簿記データを維持する必要があるため、対応するCPU命令よりもかなり高価です。ほとんどのSTMは、他の同時トランザクションとの競合をチェックし、アクセスをログに記録します。書き込みの場合は、アクセスされたメモリ位置の読み取りまたは書き込みに加えて、データの現在(または古い)値を記録します。これらの操作の一部は、高価な同期命令を使用し、共有メタデータにアクセスするため、コストがさらに増加します。これにより、シーケンシャルコードと比較すると、シングルスレッドのパフォーマンスが低下します。- なぜSTMは単なる研究玩具ではないのか-ページ1

以下の論文も参照してください。

これらの論文は数年前のものです。状況は変更/改善された可能性がありますが、すべてではありません。


「不変のデータ構造とSTMを使用した完全に優れた実行コードでも、STMがロックを使用しているため、予期しない結果が生じる可能性があります。」そのような予期しない結果の例を教えていただけますか?私の知る限りでは、STMは信頼性が高く構成可能であると考えられていますが、おそらく私が知らないことに気付いているかもしれません...?
Jules

また、STMは「ロックを使用する」と述べていますが、これは、一部のバージョンの実装の詳細にすぎません。STMは、アトミック更新を使用することにより、完全にロックフリーのシステムに実装できます。dl.acm.org/citation.cfm?id=1941579を
Jules

1

尊敬すべき研究者たちが、便利な同期メカニズムを使用してデッドロックを構築できると言っていると聞いたことがあります。トランザクションメモリ(ハードウェアまたはソフトウェア)に違いはありません。たとえば、スレッドバリアを作成するこのアプローチを考えてみます。

transaction {
    counter++;
}

while (true) {
    transaction {
        if (counter == num_threads)
            break;
    }
}

(注:この例は、PACT 2009のYannis Smaragdakisによる論文から引用しています)

これが多数のスレッドを同期するための良い方法ではないという事実を無視すると、それは正しいようです。しかし、それは構成可能ではありません。ロジックを2つのトランザクションに入れるという決定は不可欠です。これを別のトランザクションから呼び出し、すべてが1つのトランザクションにフラット化される場合、おそらく完了しません。

同じことがメッセージパッシングチャネルにも当てはまります。通信サイクルがデッドロックを引き起こす可能性があります。C ++アトミックとのアドホック同期は、デッドロックを引き起こす可能性があります。RCU、シーケンスロック、リーダー/ライターロック、条件変数、およびセマフォをすべて使用して、デッドロックを作成できます。

トランザクションやチャネル(またはロックやRCU)が悪いと言っているのではありません。むしろ、それは可能ではないと思われることがあるということです。スケーラブルで構成可能な、病理学のない同時実行制御メカニズムはおそらく不可能です。

トラブルを回避する最善の方法は、銀の弾丸のメカニズムを探すことではなく、適切なパターンを厳密に使用することです。並列コンピューティングの世界では、Arch Robison、James Reinders、Michael McCoolによるStructured Parallel Programming:Patterns for Efficient Computationが出発点として最適です。並行プログラミングについては、いくつかの良いパターンがありますが(@gardenheadのコメントを参照)、C ++およびJavaプログラマーがそれらを使用することはほとんどありません。より多くの人々が正しい方法を使い始めることができる1つのパターンは、プログラムのアドホック同期をマルチプロデューサー、マルチコンシューマーキューに置き換えることです。そして、TMは抽象化のレベルを上げるという点でロックよりも間違いなく優れているため、プログラマーはアトミックである必要があるものに集中し、原子性を確保するための巧妙なロックプロトコルの実装方法。うまくいけば、ハードウェアTMが向上し、言語がより多くのTMサポートを追加すると、TMが一般的なケースでロックに取って代わるようになるでしょう。


1
「並行プログラミングの場合、パターンはあまり開発されていません。」これは明らかに誤りです。計算の並行モデルは数十年前に遡ります。アクターモデルは70年代前半に発明され、プロセス計算のさまざまな形式が発明され、その時から研究されてきました。問題は、優れた計算モデルにはありません。それは、それらのモデルを使用することを望まないプログラマーと、実装の非効率性にあります。
gardenhead 2017年

素晴らしい点、@ gardenhead。モデルが開発されました、それは誰もそれらを使用しないというだけです。返信を編集します。
マイクスピア
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.