1. 程式人生 > >Scala:簡單使用Actor的訊息傳送與接收求和

Scala:簡單使用Actor的訊息傳送與接收求和

從Scala的諸多介紹當中,就看到了不少特別指出Scala中的Actor能夠實現並行程式設計的強大功能,它是基於事件模型的併發機制。或者說,Scala是運用訊息(message)的傳送、接收來實現多執行緒的。使用Scala能夠更容易地實現多執行緒應用的開發。 

說到並行與訊息傳送、接收,我記起了上學期“平行計算”課程中的實驗上機課中,在VC++6.0下使用的MPI機制就是基於訊息的傳送與接收來實現並行程式設計的。當初的實驗是做得一塌糊塗啊,要不現在學習Scala的Actor基於訊息的並行程式設計機制就不會那麼難理解了。可惜… 

對於Java,我們都知道它的多執行緒實現需要對共享資源(變數、物件等)使用synchronized 關鍵字進行程式碼塊同步、物件鎖互斥等等。而且,常常一大塊的try…catch語句塊中加上wait方法、notify方法、notifyAll方法是讓人很頭疼的。原因就在於Java中多數使用的是可變狀態的物件資源,對這些資源進行共享來實現多執行緒程式設計的話,控制好資源競爭與防止物件狀態被意外修改是非常重要的,而物件狀態的不變性也是較難以保證的。 

而在Scala中,我們可以通過複製不可變狀態的資源(即物件,Scala中一切都是物件,連函式、方法也是)的一個副本,再基於Actor的訊息傳送、接收機制進行並行程式設計。 

下面是我剛剛學習到Actor並行程式設計知識所做的一個模擬程式,雖然相當簡單甚至弱智(見怪不怪啊,呵呵…),但總算是自己嘗試著理解了《Programming Scala 鉛筆書》中的內容寫出來的。

程式目的:給定一個Int整數number,使用Actor計算從1到number的總和。程式碼如下:

  1. import scala.actors.Actor 
  2. def calculateSum(number: Int) = { 
  3.     val num = number 
  4.     val caller = Actor.self // 獲得當前執行緒的引用 
  5.     for(i <- 1 to num){ 
  6.         Actor.actor{ 
  7.             caller ! {      // 呼叫!傳送訊息 
  8.                 println(i)  // 列印每次傳送出去的i 
  9.                 i   // 傳送i,下面receive中case的sumInSent型別為Int 
  10.             } 
  11.         }    
  12.     } 
  13.     // 下句的 /: 等效於List.foldLeft方法 
  14.     val sum = (0 /: (1
     to num)){         
  15.         (partialSum, elem) => 
  16.             Actor.receive{  // 接收並用case匹配傳入的Int值 
  17.                 case sumInSent: Int => partialSum + elem    // 統計和 
  18.             } 
  19.     }        
  20.     println("sum = " + sum) 
  21. calculateSum(100000)

其中的 (0 /: (1 to num)) 等效於 List 中的foldLeft 方法,/: 只不過是foldLeft方法的一個簡潔寫法而已。說起來List中的這個foldLeft方法也是花了我好些時間來從試錯中理解到的(就是還沒習慣理解英文書的講述,得加強鍛鍊),特別是foldLeft方法中接下來使用Curring技巧傳入引數、函式文字所代表的含義等等。我感覺Scala的語法太簡潔以至於有些難理解…+_+ 

程式碼中已有部分註釋,不過應注意import進scala.actors.Actor 類,其中的self、!(感嘆號,傳送方法)和receive方法都是Actor類中的方法。我特地不用下劃線“_”匯入Actor類中的所有方法,而是用顯式的Actor加點的形式進行呼叫。 

嘗試了不同的輸入,程式輸出如下: 

1、故意給了個超大的整數,執行中記憶體堆溢位了,剛好被我發現並截圖了,OutOfMemoryError:Java heap space 。有趣的是,程式繼續在執行,接下來還有更加難理解的錯誤輸出呢,輸出太快來不及截圖。 

這是“瘋狂”的CPU和記憶體使用情況,天啊,我的記憶體才1 GB,它就耗掉了0.99GB,+_+… 

       2、這個是輸入為100000的執行情況,正常。

       其實我還不清楚上面的輸出是否正常,或者說我還不知道上面的程式碼是否合理,因為鉛筆書的下一節才是“Message Passing(訊息傳遞)”呢。看上面的輸出,很明顯並不是按照從 1 到100000順序輸出的,這是不是Scala訊息傳遞或者說並行程式設計的效果呢?期待接下去的學習,哈哈…