Scala學習筆記(四)react/receive對比

來源:互聯網
上載者:User

這回繼續研究Actor的應用,我發現scala-lang裡關於Actor的Advance Example很有代表性,所以專門花時間研究一下這個例子,以下我經過我修正後的代碼並且加入了一些關鍵的debug資訊,因為原始的版本無法在Scala2.8上運行:

import scala.actors._import scala.actors.Actor._object Message {  def main(args: Array[String]) {    val n = 3    var k = 0        val nActors = 500    val finalSum = n * nActors    def beh(next: Actor, sum: Int) {      k = k+1      react {        case value: Int =>          val j = value + 1; val nsum = sum + j          println(Thread.currentThread().getId()+"nSum:"+nsum+", value:"+value+", sum:"+sum)          if (next == null && nsum >= finalSum) {            println("End");            System.exit(0)          }          else {            if (next != null) next ! j            //react never return, so must call some code to continue            beh(next, nsum)          }      }    }        def actorChain(i: Int, a: Actor): Actor =      if (i > 0) actorChain(i-1, actor(beh(a, 0))) else a        val firstActor = actorChain(nActors, null)    println("#call times:"+k);    var i = n; while (i > 0) { firstActor ! 0; i -= 1 }        println("Let's go!");  }}

這個例子的意圖是實用多線程技術計算++操作,直到達到目標值finalSum為止。首先做的是遞迴的建立500個actor對象,使用的文法是actor{body}:Unit的方式,每一個actor都擁有前面建立那個的引用,這樣可以測試兩個actor互相發訊息,就像擊鼓傳花的那種效果。

 

開始啟動並執行時候,由firstActor開始,注意到這裡初始同時向firstActor發送了3次訊息,每次都是一個Int: 0,然後在接收訊息的時候,我特意列印了當前線程的ID並記錄了各個關鍵運算參數的值。可能令人費解的是react{}的使用,以及else裡beh又遞迴的調用了自己。通常來說我們在actor裡都是使用receive或者receiveWithin接收訊息,但是這兩種方式有個致命的弱點,就是每次接收一次訊息都必須使用新的Thread,所以如果我們改用receive或者receiveWithin來接收訊息,那麼你會看到幾百個Thread
ID。下面給出使用receive和receiveWithin版本的程式碼片段:(注意receiveWithin必須要加上TIMEOUT的case,否則無法運行)

//receiveWithin receiveWithin(1000) {        case value: Int =>          val j = value + 1; val nsum = sum + j          println(Thread.currentThread().getId()+"nSum:"+nsum+", value:"+value+", sum:"+sum)          if (next == null && nsum >= finalSum) {            println("End");            System.exit(0)          }          else {            if (next != null) next ! j          }                  case TIMEOUT =>        System.exit(-1);      }}//receive versionreceive {        case value: Int =>          val j = value + 1; val nsum = sum + j          println(Thread.currentThread().getId()+"nSum:"+nsum+", value:"+value+", sum:"+sum)          if (next == null && nsum >= finalSum) {            println("End");            System.exit(0)          }          else {            if (next != null) next ! j          }}}

 

所以出於效能考慮,這裡就使用了react,因為它會使用一種更為合理的任務領取方式來申請Thread資源,多次訊息的發送和接受都是由總的線程池來安排最佳化的線程數量來執行。在筆者的機器上一共就看到4個不同的Thread ID。另外,使用react的一個特點是它每次執行完了case,不會return任何東西,也就是說代碼不能在繼續往下走。這個時候一個通常的做法都是遞迴的調用包含react的自身,這樣就會回到react的起點,然後等待線程池安排任何一個Thread來領取任務,完成訊息的處理。

 

由於使用了react,只用少量的Thread資源就反覆的處理了眾多的訊息最後完成值計算超過finalSum成功End退出。在筆者的機器上call times的值是會變化而且多執行幾次次數還相差不小。這是因為眾多訊息的處理是由隨機的Thread對象來領取的,可能是由next ! j引起的,那麼這個Thread就能獲得當前beh對象的上下文以及sum, nSum的值;同時,也有可能是遞迴調用beh(next, nSum)引起的,那麼這個Thread拿到的是另外一套上下文和變數值。總之誰先到finalSum,程式就結束。這個值可以通過筆者列印出來的log仔細分析一下,尤其是同receive和receiveWithin版本對比一下,相信你會有所收穫的。 

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.