scala学習十一同時プログラミング

9167 ワード

まず、生産者の消費者の例を示します.
Producer:
  class Producer(drop : Drop) 
    extends Runnable 
  { 
    val importantInfo : Array[String] = Array( 
      "Mares eat oats", 
      "Does eat oats", 
      "Little lambs eat ivy", 
      "A kid will eat ivy too"
    ); 
  
    override def run() : Unit = 
    { 
      importantInfo.foreach((msg) => drop.put(msg)) 
      drop.put("DONE") 
    } 
  } 
Consumer:
  class Consumer(drop : Drop) 
    extends Runnable 
  { 
    override def run() : Unit = 
    { 
      var message = drop.take() 
      while (message != "DONE") 
      { 
        System.out.format("MESSAGE RECEIVED: %s%n", message) 
        message = drop.take() 
      } 
    } 
  } 
Drop:
 class Drop 
  { 
    var message : String = ""
    var empty : Boolean = true 
    var lock : AnyRef = new Object() 
  
    def put(x: String) : Unit = 
      lock.synchronized 
      { 
        // Wait until message has been retrieved 
        await (empty == true) 
        // Toggle status 
        empty = false 
        // Store message 
        message = x 
        // Notify consumer that status has changed 
        lock.notifyAll() 
      } 

    def take() : String = 
      lock.synchronized 
      { 
        // Wait until message is available. 
        await (empty == false) 
        // Toggle status 
        empty=true 
        // Notify producer that staus has changed 
        lock.notifyAll() 
        // Return the message 
        message 
      } 

    private def await(cond: => Boolean) = 
      while (!cond) { lock.wait() } 
  } 
主Object:
object ProdConSample 
 { 
  def main(args : Array[String]) : Unit = 
  { 
    // Create Drop 
    val drop = new Drop(); 
  
    // Spawn Producer 
    new Thread(new Producer(drop)).start(); 
    
    // Spawn Consumer 
    new Thread(new Consumer(drop)).start(); 
  } 
 }
ProducerクラスとConsumerクラスは、Javaクラスとほぼ同じであり、Runnableインタフェースを再拡張(実装)し、run()メソッドをカバーし、Producerの場合には、importantInfo配列のコンテンツを巡回するために内蔵反復メソッドをそれぞれ使用する.(実際には、Scalaのようにするために、importantInfoListではなくArrayであるべきかもしれませんが、最初の試みでは、元のJavaコードと一致することをできるだけ保証したいと思っています.)Dropクラスは、Javaバージョンと同様です.しかし、Scalaにはいくつかの例外があり、「synchronized」はキーワードではなく、AnyRefクラスに対して定義された方法、すなわちScala「すべての参照タイプのルート」です.これは、特定のオブジェクトを同期するには、そのオブジェクトに同期メソッドを呼び出すだけです.この例では、Dropのlockフィールドに保存されているオブジェクトに対して同期メソッドを呼び出す.await()メソッドで定義されたDropクラスでは、condパラメータは、メソッドに渡される前に計算を行うのではなく、計算を待つコードブロックであるというScalaメカニズムも利用していることに留意されたい.Scalaでは、これを「call-by-name」と呼ぶ.ここでは、Javaバージョンで2回表示する必要がある条件待ち論理(それぞれputおよびtake)をキャプチャする実用的な方法である.
最後に、main()Dropインスタンスを作成し、2つのスレッドをインスタンス化し、start()を使用してそれらを起動し、main()の終了部分で終了し、JVMはmain()が終了する前に2つのスレッドを起動すると信じられる.(本番コードでは、このような状況は保証できないかもしれませんが、このような簡単な例では99.99%は問題ありません.)
しかし、プログラマは依然として2つのスレッド間の通信と協調の問題を過度に心配する必要があると述べた.一部のScalaメカニズムは文法を簡略化できるが,これまではそれほど魅力的ではなかった.
Scala同時性v 2
Scala Library Referenceには興味深いバッグがあります:scala.concurrency.このパッケージには、MailBoxクラスを含む多くの異なる同時構造が含まれています.
文字通り、MailBoxは本質的にDropであり、検出前にデータブロックの単一スロットバッファを保存するために使用される.しかしながら、MailBoxの最大の利点は、単純なDrop(またはDropのマルチスロットデータ保存クラスjava.util.concurrent.BoundedBuffer)よりも柔軟に、送信および受信データの詳細をモードマッチングおよびcaseクラスに完全にカプセル化することである.
 package com.tedneward.scalaexamples.scala.V2 
 { 
  import concurrent.{MailBox, ops} 

  object ProdConSample 
  { 
    class Producer(drop : Drop) 
      extends Runnable 
    { 
      val importantInfo : Array[String] = Array( 
        "Mares eat oats", 
        "Does eat oats", 
        "Little lambs eat ivy", 
        "A kid will eat ivy too"
      ); 
    
      override def run() : Unit = 
      { 
        importantInfo.foreach((msg) => drop.put(msg)) 
        drop.put("DONE") 
      } 
    } 
    
    class Consumer(drop : Drop) 
      extends Runnable 
    { 
      override def run() : Unit = 
      { 
        var message = drop.take() 
        while (message != "DONE") 
        { 
          System.out.format("MESSAGE RECEIVED: %s%n", message) 
          message = drop.take() 
        } 
      } 
    } 

    class Drop 
    { 
      private val m = new MailBox() 
      
      private case class Empty() 
      private case class Full(x : String) 
      
      m send Empty()  // initialization 
      
      def put(msg : String) : Unit = 
      { 
        m receive 
        { 
          case Empty() => 
            m send Full(msg) 
        } 
      } 
      
      def take() : String = 
      { 
        m receive 
        { 
          case Full(msg) => 
            m send Empty(); msg 
        } 
      } 
    } 
  
    def main(args : Array[String]) : Unit = 
    { 
      // Create Drop 
      val drop = new Drop() 
      
      // Spawn Producer 
      new Thread(new Producer(drop)).start(); 
      
      // Spawn Consumer 
      new Thread(new Consumer(drop)).start(); 
    } 
  } 
 }

ここで、v 2とv 1との間の唯一の違いは、Dropの実装であり、MailBoxクラスを使用して、Dropから削除されたメッセージのブロックおよび信号トランザクションを処理する.(ProducerおよびConsumerを直接使用するように書き換えることができるが、単純性を考慮すると、すべての例におけるMailBox APIを一致させることが望ましいと仮定する.)Dropの使用は、典型的なMailBox(BoundedBuffer)の使用とは少し異なるので、コードをよく見てみましょう.Dropには、MailBoxsendの2つの基本的な動作があります.receiveの方法は、タイムアウトに基づくreceiveWithinにすぎない.receiveは、任意のタイプのメッセージを受信する.MailBoxメソッドは、メッセージをメールボックスに配置し、そのタイプのメッセージに関心のある待機受信者に直ちに通知し、後で取得するためにメッセージチェーンテーブルに添付する.send()方法は、機能ブロックに適切なメッセージが受信されるまでブロックされる.
したがって、この場合、2つのcaseクラスを作成します.1つはコンテンツを含まない(receive())、これはEmptyが空であることを示し、もう1つはメッセージデータを含む(MailBox).Fullメソッドは、putにデータを配置するため、Dropに対してMailBoxを呼び出してreceive()インスタンスを検索するため、Emptyが送信されるまでブロックされる.このとき、Emptyのインスタンスは、新しいデータを含むFullに送信される. MailBoxメソッドは、takeからデータが削除されるので、Dropに対してMailBoxインスタンスを呼び出してreceive()インスタンスを検索し、メッセージを抽出し(caseクラスの内部から値を抽出し、ローカル変数にバインドする能力に再びモードマッチングのおかげで)、FullインスタンスをEmptyインスタンスに送信する.
明確なロックは不要であり、モニタを考慮する必要はありません.
Scala同時性v 3
実際には、MailBoxProducerが機能的なクラスを必要としない限り、コードを著しく短縮することができます(ここではそうです).両者は本質的にConsumerメソッドの痩せた包装器であり、ScalaはRunnable.run()オブジェクトのscala.concurrent.opsメソッドを使用して実装することができます.
 package com.tedneward.scalaexamples.scala.V3 
 { 
  import concurrent.MailBox 
  import concurrent.ops._ 

  object ProdConSample 
  { 
    class Drop 
    { 
      private val m = new MailBox() 
      
      private case class Empty() 
      private case class Full(x : String) 
      
      m send Empty()  // initialization 
      
      def put(msg : String) : Unit = 
      { 
        m receive 
        { 
          case Empty() => 
            m send Full(msg) 
        } 
      } 
      
      def take() : String = 
      { 
        m receive 
        { 
          case Full(msg) => 
            m send Empty(); msg 
        } 
      } 
    } 
  
    def main(args : Array[String]) : Unit = 
    { 
      // Create Drop 
      val drop = new Drop() 
      
      // Spawn Producer 
      spawn 
      { 
        val importantInfo : Array[String] = Array( 
          "Mares eat oats", 
          "Does eat oats", 
          "Little lambs eat ivy", 
          "A kid will eat ivy too"
        ); 
        
        importantInfo.foreach((msg) => drop.put(msg)) 
        drop.put("DONE") 
      } 
      
      // Spawn Consumer 
      spawn 
      { 
        var message = drop.take() 
        while (message != "DONE") 
        { 
          System.out.format("MESSAGE RECEIVED: %s%n", message) 
          message = drop.take() 
        } 
      } 
    } 
  } 
 }
spawn
メソッド(ブロック上部のspawn
オブジェクトインポート)あるコードブロック(別のby-nameパラメータの例)を受信し、匿名で構築されたスレッドオブジェクトのops
メソッドの内部.実は、理解しにくいわけではありません.run()
の定義はspawn
クラスの内部はどのようなものですか.
<span style="background-color: rgb(153, 153, 153);">  def spawn(p: => Unit) = { 
    val t = new Thread() { override def run() = p } 
    t.start() 
  }</span>

実際、Scalaの同時サポートはops
およびMailBox
クラス;Scalaはまた、同様の「Actors」概念をサポートし、ops
採用された方法は、類似のメッセージング方法であるが、より包括的で柔軟性が高い.