1. 程式人生 > >Scala學習筆記(actor)

Scala學習筆記(actor)

7.actor

Scala中處理併發,有很多選擇:

lactor訊息模型,類似Erlang,首選,Liftakka也實現了自己的actor模型。

lThreadRunnable

ljava.util.concurennt

l3rd併發框架如NettyMina

7.1.actor模型

Java內建執行緒模型

Scala actor模型

“共享資料-鎖”模型(share data and lock

share nothing

每個object有一個monitor,監視多執行緒對共享資料的訪問

不共享資料,actor之間通過message通訊

加鎖的程式碼段用

synchronized標識

死鎖問題

每個執行緒內部是順序執行的

每個actor內部是順序執行的

7.2.多核計算

對比如下的演算法:

def perfect(n:Int) =

n==(1 until n filter (n%_==0) sum)

val n = 33550336

// 序列計算

n to n+10 foreach (i=>println(perfect(i)))

def perfect(n:Int) =

n==(1 until n filter (n%_==0) sum)

val n = 33550336

// 平行計算

class T1(n:Int) extends Thread {

  override def run(){println(perfect(n))}}

n to n+10 foreach (i=>new T1(i).start)

耗時:8297

耗時:5134

單執行緒序列計算,不能很好發揮多核優勢

多執行緒平行計算,平均分配到多核,更快

上面是Java的寫法,也可以用Scalaactor寫法:

Scala寫法1

import actors.Actor,actors.Actor._

class A1 extends Actor {

def act { react { case n:Int=>println(perfect(n)) }}}

n to n+10 foreach (i=>{ (new A1).start ! i})

Scala寫法2

val aa = Array.fill(11)(actor { react { case n:Int=>println(perfect(n)) }})

n to n+10 foreach (i=>aa(i-n) ! i)

或者:

n to n+10 foreach (i=> actor { react { case n:Int=>println(perfect(n)) }} ! i)

7.3.Actor用法

Scala會建立一個執行緒池共所有Actor來使用。receive模型是Actor從池中取一個執行緒一直使用;react模型是Actor從池中取一個執行緒用完給其他Actor

實現方式1

importscala.actors._

objectActor1extendsActor { // 或者class

// 實現執行緒

defact() { react { case _ =>println("ok"); exit} }

}

//傳送訊息:

Actor1.start ! 1001 // 必須呼叫start

實現方式2

importscala.actors.Actor._

vala2 = actor { react { case _ =>println("ok") } } // 馬上啟動

//傳送訊息:

a2 ! "message" // 不必呼叫start

提示:

!

傳送非同步訊息,沒有返回值。

!?

傳送同步訊息,等待返回值。(會阻塞傳送訊息語句所在的執行緒)

!!

傳送非同步訊息,返回值是 Future[Any]

?

不帶引數。檢視 mailbox 中的下一條訊息。

7.4.方式1:接受receive

特點:要反覆處理訊息,receive外層用while(..)

import actors.Actor, actors.Actor._

val a1 = Actor.actor {

varwork = true

while(work) {

receive { // 接受訊息或者用receiveWith(1000)

casemsg:String => println("a1: "+msg)

casex:Int => work = false;println("a1 stop: "+x)

}

}

}

a1 ! "hello" // "a1: hello"

a1 ! "world" // "a1: world"

a1 ! -1 // "a1 stop: -1"

a1 ! "no response :("

7.5.方式2:接受react, loop

特點:

l從不返回

l要反覆執行訊息處理,react外層用loop不能while(..);

l通過複用執行緒,比receive更高效,應儘可能使用react

import actors.Actor, Actor._

val a1 = Actor.actor {

react {

case x:Int => println("a1 stop: " + x)

case msg:String => println("a1: " + msg); act()

    }

}

a1 ! "hello" // "a1: hello"

a1 ! "world" // "a1: world"

a1 ! -1 // "a1 stop: -1"

a1 ! "no response :("

如果不用退出的執行緒,可使用loop改寫如下:

val a1 = Actor.actor {

  loop {

react {

casex:Int => println("a1 stop: "+x); exit()

case msg:String => println("a1: " + msg)

    }

  }

}

7.6.REPL接受訊息

scala> self ! "hello"

scala> self.receive { case x => x }

scala> self.receiveWithin(1000) { case x => x }

7.7.actor最佳實踐

7.7.1.不阻塞actor

actor不應由於處理某條訊息而阻塞,可以呼叫helper-actor處理耗時操作(helper actor雖然是阻塞的,但由於不接受訊息所以沒問題),以便actor接著處理下一條訊息

-----------------------------------------

import actors._, actors.Actor._

val time = 1000

// 1原來阻塞的程式

val mainActor1 = actor {

    loop { react {

case n: Int => Thread.sleep(time)

                         println(n)

case s => println(s) } }

  }

  1 to 5 foreach { mainActor1 ! _ } // 5秒鐘後列印完數字

// 2改寫由helper actor去阻塞的程式

val mainActor2: Actor = actor {

    loop { react {

case n: Int => actor { Thread.sleep(time); mainActor2 ! "wakeup" }

                        println(n)

case s => println(s) } }

  }

  1 to 5 foreach { mainActor2 ! _ } // 馬上列印數字; 1秒鐘後列印5wakeup

-----------------------------------------

7.7.2.actor之間用且僅用訊息來通訊

actor模型讓我們寫多執行緒程式時只用關注各個獨立的單執行緒程式(actor),他們之間通過訊息來通訊。例如,如果BadActor中有一個GoodActor的引用:

class BadActor(a:GoodActor) extends Actor {...}

那在BadActor中即可以通過該引用來直接呼叫GoodActor的方法,也可以通過“!”來傳遞訊息。選擇後者!因為一旦BadActor通過引用讀取GoodActor例項的私有資料,而這些資料可能正被其他執行緒改寫值,結果就避免不了“共享資料-鎖”模型中的麻煩事:即必須保證BadActor執行緒讀取GoodActor的私有資料時,GoodActor執行緒在這塊成為“共享資料”的操作上加鎖。GoodActor只要有了共享資料,就必須來加鎖防範競用衝突和死鎖,你又得從actor模型退回到“共享資料-鎖”模型(注:actor對訊息是順序處理的,本來不用考慮共享資料)。

7.7.3.採用不可變訊息

Scalaactor模型讓每個actoract方法內部接近於單執行緒環境,你不用當心act方法裡面的操作是否執行緒安全。在act方法中你可以盡情使用非同步、可變物件,因為每個act方法被有效限制在單個執行緒中,這也是actor模型被稱為“share-nothing模型(零共享模型)的原因,其資料的作用範圍被限制在單個執行緒中。不過一旦物件內的資料被用於多個actor之間進行訊息傳遞。這時你就必須考慮訊息物件是否執行緒安全。

保證訊息物件執行緒安全的最好方法就是保證只使用不可變物件作為訊息物件。訊息類中只定義val欄位,且只能指向不可變物件。定義這種不可變訊息類的簡單方法就是使用case class並保證其所有的val欄位都是不可變的。Scala API中提供了很多不可變物件可用,例如基本型別、StringTupleList,不可變Set、不可變Map等。

如果你發現確實需要把一個可變物件obj1傳送給其他actor,也因該是傳送一份拷貝物件obj1.clone過去,而不是把obj1直接發過去。例如,資料物件Array是可變且未做同步的,所以Array只應該由一個actor同時存取,如果需要傳送陣列arr,就傳送arr.clonearr中的元素也應該是不可變物件),或者直接傳送一個不可變物件arr.toList更好。

總結:大部分時候使用不可變物件很方便,不可變物件是並行系統的曙光,它們是易使用、低風險的執行緒安全物件。當你將來要設計一個和並行相關的程式時,無論是否使用actor,都應該儘量使用不可變的資料結構。

7.7.4.讓訊息自說明

對每一種訊息建立一個對應的case class,而不是使用上面的tuple資料結構。雖然這種包裝在很多情況下並非必須,但該做法能使actor程式易於理解,例如:

// 不易理解,因為傳遞的是個一般的字串,很難指出那個actor來響應這個訊息

lookerUpper ! ("www.scala-lang.org", self)

// 改為如下,則指出只有react能處理LoopupIPactor來處理:

caseclass LookupIP(hostname: String, requester: Actor)

lookerUpper ! LookupIP("www.scala-lang.org", self)

7.8.不同jvm間的訊息訪問

伺服器端:

objectActorServerextendsApplication {

importactors.Actor, actors.Actor._, actors.remote.RemoteActor

Actor.actor { // 建立並啟動一個 actor

// 當前 actor 監聽的埠: 3000

RemoteActor.alive(3000)

//  3000 埠註冊本 actor,取名為 server1

// 第一個引數為 actor 的標識,它以單引號開頭,是 Scala 中的 Symbol 量,

// Symbol 量和字串相似,但 Symbol 相等是基於字串比較的。

// self 指代當前 actor (注意此處不能用 this

RemoteActor.register('server1Actor.self)

// 收到訊息後的響應

loop {

Actor.react {casemsg =>

println("server1 get: "+msg)

        }

相關推薦

Scala學習筆記actor

7.actor Scala中處理併發,有很多選擇: lactor訊息模型,類似Erlang,首選,Lift和akka也實現了自己的actor模型。 lThread、Runnable ljava.util.concurennt l3rd併發框架如Netty,M

Scala學習筆記編程基礎

大數據 上一個 extends 移除 condition api arr 調用方法 tab 強烈推薦參考該課程:http://www.runoob.com/scala/scala-tutorial.html 1. Scala概述 1.1. 什麽是Scala Scala

Scala學習筆記1

ont arr oat rim import 經歷 變參 文件 變量類型 Scala的基礎語法 1、變量類型 AnyVal 共有9種:   Byte、Short、Int、Long、Float、Double、Char、Boolean、Unit(void) AnyRef   S

Scala學習筆記—— 對映、元祖、集合

1. 對映 對映Java中的Map,即Key/Value的資料形式 對映的建立,有以下兩種方法 1 scala> val map =Map("Lisa" -> 90 , "Hellen" -> 89) 2 map: scala.collection.immutabl

Scala學習筆記2——一口氣講完用ItellijMaven建工程並打包的事情

在進行本地Scala環境配置時,需要完成以下幾個步驟: 1、安裝JDK,目前僅支援1.8版本; 2、安裝Scala,並配置對應的環境變數; 3、安裝Spark,並配置對應的環境變數; 4、安裝Intellij,並下載對應的scala plugin。 詳細可見: https:/

scala學習筆記1——為什麼要學習scala

      自從大資料、人工智慧、區塊鏈等概念一炒再炒,python無疑成為了最為熱門的語言,常年盤踞程式語言前三,與此同時,活躍的社群、便於入門的程式碼也使得python成為進入人工智慧、大資料領域的最佳語言選擇,但是筆者在大資料領域的一個最深切感受卻是:如果要在大資

Scala學習筆記:入門

變數定義 Scala有兩種變數,val和var。val類似於Java中的final變數,一旦初始化了,val就不能再被賦值。var可以多次賦值。但由於函數語言程式設計特性,Scala更推崇val。 var的使用也有其侷限性,Scala有型別推斷的功能,當var被初始化後,其型別就已經被斷定,比如 當m被

Scala學習筆記:類和物件

object object 是隻有一個例項的類。它的定義與Java中的class類似,如: // 單例物件 object AppEntry { def main(args: Array[String]): Unit = { print("Hello World!") } }

Scala學習筆記:==,eq與equals的區別

== Scala中==與java中不同,它是比較值是否相等的,無論比較物件是否是相同型別 List(1, 2, 3) == List(1, 2, 3) //true 1==1.0//true equals 同類型 與==作用相同,都是比較值是否相同 不同型別 返回false,如 1.equal

Scala學習筆記:apply方法說明

調用 我們 val sca 關鍵字 語法糖 方式 rgs 類型 當scala中類或者對象有一個主要用途的時候,apply方法就是一個很好地語法糖。請看下面一個簡單的例子: class Foo(foo: String) {} object Foo { def appl

Scala學習筆記:本地函式、頭等函式、佔位符和部分應用函式

本地函式 可以在方法內定義方法,這種方法叫本地函式,本地函式可以直接訪問父函式的引數 def parent(x: Int, y: Int): Unit ={ def child(y:Int) = y + 1 val z = child(y) println(s"x: $x, z

Scala學習筆記1—— Scala 介紹和安裝

1 Scala 介紹 Scala是一種多正規化的程式語言,其設計的初衷是要整合面向物件程式設計和函數語言程式設計的各種特性。Scala運行於Java平臺(Java虛擬機器),併兼容現有的Java程式。

Scala學習筆記4—— scala 練習

1 練習 1.1 建立一個List scala> val lst0 = List(1,7,9,8,0,3,5,4,6,2) lst0: List[Int] = List(1, 7, 9, 8, 0

Scala學習筆記10—— Akka 實現簡單 RPC 框架

1 Akka 介紹 目前大多數的分散式架構底層通訊都是通過RPC實現的,RPC框架非常多,比如前我們學過的Hadoop專案的RPC通訊框架,但是Hadoop在設計之初就是為了執行長達數小時的批量而設計的,在某些極端的情況下,任務提交的延遲很高,所有Hadoop的

Scala學習筆記11—— RPC 通訊框架

1 通訊業務邏輯 定義2個類 Master, Worker。首先啟動Master,然後啟動Worker Worker 啟動後,在 preStart 方法中與 Master 建立連線,向 Master 傳送註冊,將 Worker 的資訊通過 case class

Scala學習筆記12—— scala 高階特性

1 高階函式 Scala混合了面向物件和函式式的特性,通常將可以做為引數傳遞到方法中的表示式叫做函式。在函數語言程式設計語言中,函式是“頭等公民”,高階函式包含:作為值的函式、匿名函式、閉包、柯里化等等。 1.1 作為值的函式 可以像任何其他資料型別一樣被傳遞和

Scala學習筆記13——隱式轉換

1 泛型 [T <: UpperBound] [T >: LowerBound] [T <% ViewBound] [T : ContextBound] [+T] [-T] 1.1

Scala學習筆記——Scala基礎

Scala基礎 1. 常用資料型別 Scala與Java有著相同的常用資料型別: Byte、Short、Int、Long、Float、Double、Chat、Boolean(只有包裝型別,無原始型別) 2. 宣告變數 //var 即vari

Scala學習筆記:控制結構和函式

2.1 條件表示式       2.1.1 Scala中if/else語法結構和java一樣,不同點在於此if表示式有值:           val s=if (x>0) 1 else -1        //s的值要麼是1,要麼是-1,取決於X的範圍    

Scala學習筆記react/receive對比

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