2017-07-31 125 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的特征;这并不违反每个消息一次处理的行为者原则。