Scalaアクター:受信vs反応


110

まず、Javaの経験は豊富ですが、関数型言語に興味を持つようになったのはつい最近のことです。最近、Scalaを検討し始めました。Scalaはとてもいい言語のようです。

ただし、「Scalaでのプログラミング」でScalaのActorフレームワークについて読んでいますが、理解できないことが1つあります。30.4章では、react代わりにreceiveを使用するとスレッドを再利用できるようになると述べています。これは、JVMではスレッドが高価であるため、パフォーマンスに優れています。

これは、のreact代わりに呼び出すことを覚えている限り、receive好きなだけアクターを開始できることを意味しますか?Scalaを発見する前は、Erlangで遊んでおり、Programming Erlangの作者は200,000を超えるプロセスをスポーンさせずに生み出したことを誇りに思っています。Javaスレッドでそれを行うのは嫌です。Erlang(およびJava)と比較して、Scalaではどのような制限を見ていますか?

また、このスレッドの再利用はScalaでどのように機能しますか?簡単にするために、スレッドが1つしかないと仮定します。私が開始するすべてのアクターはこのスレッドで順次実行されますか、それとも何らかのタスク切り替えが行われますか?たとえば、メッセージを互いにピンポンする2つのアクターを開始した場合、それらが同じスレッドで開始された場合、デッドロックの危険がありますか?

Scalaのプログラミングによると、使用するアクターの作成はを使用する場合reactよりも困難ですreceivereact戻らないので、これはもっともらしく聞こえます。ただし、この本では、をreact使用してループ内に配置する方法を示していますActor.loop。結果として、

loop {
    react {
        ...
    }
}

私にとって、これは

while (true) {
    receive {
        ...
    }
}

これは本の前半で使用されています。それでも、本は「実際には、プログラムには少なくとも数個が必要になる」と述べていreceiveます。だから私はここで何が欠けていますか?返す以外に、receiveそれreactができないことは何ですか?そして、なぜ私は気にしますか?

最後に、私が理解していないことの核心にたどり着きます。この本ではreact、スレッドを再利用するためにコールスタックを破棄することがどのように可能になるかについて、本が繰り返し言及しています。それはどのように機能しますか?呼び出しスタックを破棄する必要があるのはなぜですか?そして、関数が例外(react)をスローすることによって終了したときにコールスタックが破棄されるのはなぜreceiveですか?

Scalaでのプログラミング』は、ここでの主要な問題のいくつかにつまづいているように思えますが、それは残念です。


回答:


78

まず、待機してreceiveいる各アクターがスレッドを占有しています。何も受信しない場合、そのスレッドは何もしません。上のアクターreactは、何かを受け取るまでスレッドを占有しません。何かを受け取ると、それにスレッドが割り当てられ、その中で初期化されます。

ここで、初期化の部分が重要です。受信スレッドは何かを返すことが期待されていますが、反応スレッドはそうではありません。つまり、最後のスタック状態の最後のスタック状態reactは完全に破棄される可能性があり、破棄されます。スタック状態を保存または復元する必要がないため、スレッドの起動が速くなります。

パフォーマンスを向上させるには、さまざまな理由があります。ご存知のように、Javaでスレッドが多すぎることはお勧めできません。一方、アクターはスレッドにアタッチする前にアクターをアタッチする必要があるため、メッセージよりもメッセージのreact方が高速です。したがって、多くのメッセージを受信するがそれをほとんど使用しないアクターがある場合、の追加の遅延により、目的に対して遅すぎる可能性があります。receivereactreact


21

答えは「はい」です。アクターがコード内の何もブロックしておらず、を使用している場合は、単一スレッド内で「並行」プログラムをreact実行できます(調べるためにシステムプロパティを設定してみてください)。actors.maxPoolSize

呼び出しスタックを破棄する必要あるより明白な理由の1つは、それ以外の場合、loopメソッドがで終了することStackOverflowErrorです。現状では、フレームワークはreactをスローすることで巧妙にを終了しますSuspendActorException。これは、ループするコードによってキャッチされreactandThenメソッドを介して再度実行されます。

でメソッドを確認mkBodyActor、次にメソッドでseqループがどのように再スケジュールされるかを確認してください-非常に賢いものです!


20

これらの「スタックを破棄する」という発言はしばらく混乱しましたが、私はそれを理解したと思います。これが私の理解です。「受信」の場合、メッセージに専用のスレッドブロックがあり(モニターでobject.wait()を使用)、これは、完全なスレッドスタックが利用可能であり、受信時に「待機」のポイントから続行する準備ができていることを意味します。メッセージ。たとえば、次のコードがある場合

  def a = 10;
  while (! done)  {
     receive {
        case msg =>  println("MESSAGE RECEIVED: " + msg)
     }
     println("after receive and printing a " + a)
  }

スレッドは、メッセージが受信されるまで受信呼び出しを待機し、その後、「受信して10を印刷した後」のメッセージを続け、スレッドがブロックされる前にスタックフレームにある値「10」を出力します。

反応の場合、そのような専用スレッドはありません。reactメソッドのメソッド本体全体がクロージャーとしてキャプチャーされ、メッセージを受信する対応するアクターの任意のスレッドによって実行されます。つまり、クロージャーとしてのみキャプチャできるステートメントのみが実行され、そこで「Nothing」の戻り値の型が機能するようになります。次のコードを検討してください

  def a = 10;
  while (! done)  {
     react {
        case msg =>  println("MESSAGE RECEIVED: " + msg)
     }
     println("after react and printing a " + a) 
  }

reactの戻り値の型がvoidの場合、「react」呼び出しの後にステートメントを置くことは合法であることを意味します(例では、「react and 10を印刷した後」のメッセージを出力するprintlnステートメント)。 "react"メソッドの本体のみがキャプチャされ、後で(メッセージの到着時に)実行するためにシーケンス化されるため、決して実行されません。reactのコントラクトの戻り値の型は "Nothing"であるため、reactに続くステートメントは存在できず、スタックを維持する理由はありません。上記の例では、react呼び出しがまったく実行されなかった後のステートメントとして、変数 "a"を維持する必要はありません。反応本体で必要なすべての変数はすでにクロージャーとしてキャプチャされているため、問題なく実行できることに注意してください。

JavaアクターフレームワークのKilimは、実際には、反応でアンロールされるスタックを保存してスタックのメンテナンスを行い、メッセージを取得します。


ありがとう、それは非常に有益でした。しかし、あなたは意味しなかった+a代わりに、コードスニペットでは+10
jqno

すばらしい答えです。どちらにもならない。
サンティアゴバスルト2012年

8

ここにそれを持っているだけです:

制御の反転なしのイベントベースのプログラミング

これらのペーパーは、ActorのScala APIからリンクされており、Actor実装の理論的なフレームワークを提供します。これには、reactが戻らない理由が含まれます。


そして二番目の論文。不正なスパム制御... :( [スレッドとイベントを統合するアクター ] [2] [2]:lamp.epfl.ch/~phaller/doc/haller07coord.pdf「スレッドとイベントを
統合するアクター

0

私はscala / akkaで大きな作業を行っていませんが、俳優のスケジュール方法に非常に大きな違いがあることを理解しています。Akkaは、アクターの実行をタイムスライスするスマートスレッドプールです...命令ごとに実行できるErlangとは異なり、タイムスライスはすべて、アクターによる1つのメッセージ実行から完了までです。

これは、現在のスレッドが他のアクターのスケジューリングを検討するようにヒントを与えるので、reactの方が良いと考えるようになります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.