2017-07-31 82 views
0

我知道actor模型的一個優點是,一次只處理一條消息,簡化了併發問題。但在我看來,我的演員正在處理多個消息。在僞代碼中,我有演員同時處理多條消息

var status = 0 
def receive = { 
    case DoSomething => 
    val dest = sender() 
    status = 0 

    for { 
     otherActor <- resolveOtherActor("/user/OtherActor") 
    } yield { 
     for { 
     res <- {status = 1 
       otherActor ? doSomething1 
       } 
     res <- {status = 2 
       otherActor ? doSomething2 
       } 
     } yield { 
     dest ! status 
     } 
    } 

    case GetStatus => sender() ! status 
} 

如果我發送一個DoSomething的消息,這個演員,然後立即發送到的getStatus這個演員再三,我會看到狀態0,1和2回來的序列。如果actor模型一次只處理一條消息,我只會看到狀態2被返回,因爲我不能訪問中間狀態。

看來,演員模式仍然需要鎖。我錯過了什麼?

+1

鎖是沒有必要的。問題模式創建一個Future,此時上下文更改和GetStatus消息可以進入,也許這可能是問題。嘗試消除詢問操作(?)並再試一次。 – EmiCareOfCell44

+0

@ EmiCareOfCell44這是我認爲可能發生的事情。然而,在一個使用許多阿卡結構的系統中,有很多'問'和其他未來的操作。所以看起來行動者似乎並不解決併發問題,除非他們不在期貨上操作,這可能不是現實的情況。 – Erix

+1

在這種情況下,我不會使用演員內部的問題。如果接收方法必須發送其他消息,我將使用tell(!)來代替。如果你需要知道「otherActor」中發生了什麼,這應該發回你需要處理的其他消息來添加另一個'case'子句。應該避免使用可變狀態的期貨。 – EmiCareOfCell44

回答

5

當您關閉某個actor的可變狀態並將其暴露給其他線程時,所有投注都將關閉,這是您的代碼在嵌套Future內變化status時所做的操作。阿卡documentation明確警告這一點。

的演員確實一次處理一個消息:

var status = 0 
def receive = { 
    case IncrementStatus => 
    status += 1 
    case GetStatus => 
    val s = status 
    sender ! s 
} 

發送一個IncrementStatus,另一IncrementStatus,然後從相同的發送者到上述演員一個GetStatus消息將導致該發件人接收2

但是,嘗試使用Future做同樣的事情並不能保證獲得相同的結果,因爲Future是異步完成的。例如:

object NumService { 
    // calculates arg + 1 in the future 
    def addOne(arg: Int): Future[Int] = { 
    Future { arg + 1 } 
    } 
} 

class MyActor extends Actor { 
    var status = 0 
    def receive = { 
    case IncrementStatusInFuture => 
     val s = status 
     NumService.addOne(s) 
       .map(UpdateStatus(_)) 
       .pipeTo(self) 

    case UpdateStatus(num) => 
     status = num 

    case GetStatus => 
     val s = status 
     sender ! s 
    } 
} 

我們mapFuture創建Future[UpdateStatus],然後pipe到演員本身Future的結果。

如果我們發送IncrementStatusInFuture,另一IncrementStatusInFuture,然後GetStatus消息MyActor從同一個發件人,我們不能保證發件人將收到一個2。參與者按順序處理這三條消息,但在參與者處理GetStatus消息時,可能尚未完成對NumService.addOne的一個或兩個調用。這種不確定的行爲是Future的特徵;這並不違反每個消息一次處理的行爲者原則。