forward 与 ! (tell) 的差异,举个例子:
Main(当前actor): topNode ! Insert(requester, id=1, ele = 2)
topNode: root ! Insert(requester, id=1, ele = 2)
对于root而言,Insert消息的来源是topNode
假如第二行替换为
topNode: root forward Insert(requester, id=1, ele = 2)
此时,对于root而言,Insert消息的来源是Main
从上面的例子可以看出,! 和 forward之间是有差别的,但是差别本身比较tricky,会增加理解的成本,所以传递消息时,把消息的actorRef作为参数传递会简单很多。
在lab4中,就需要考虑到!与forward的异同点
testcase 的 code
test("proper inserts and lookups") { val topNode = system.actorOf(Props[BinaryTreeSet]) topNode ! Contains(testActor, id = 1, 1) expectMsg(ContainsResult(1, false)) topNode ! Insert(testActor, id = 2, 1) topNode ! Contains(testActor, id = 3, 1) expectMsg(OperationFinished(2)) expectMsg(ContainsResult(3, true)) }
结合topNode,与root的处理逻辑
val normal: Receive = { // 这个forward非常重要,不能写成 !,不然第一个testcase都不过去 case operation: Operation => root ! operation case GC => { //garbageCollecting需要先变,因为节点的复制可能会很久 val newRoot = createRoot context.become(garbageCollecting(newRoot)) root ! CopyTo(newRoot) } case _ => println("unknown operation") }
// root 的处理逻辑
case Insert(requester, id, elem) => if(this.elem == elem) { if(this.removed) this.removed = false requester ! OperationFinished(id) } else { val child = if(this.elem > elem) Left else Right if(subtrees contains child) subtrees(child) ! Insert(requester, id, elem) else { subtrees += child -> context.actorOf(props(elem, false)) requester ! OperationFinished(id) } }
在source code中,topNode收到消息后,把消息传递给root,root认为消息的来源是topNode。假如消息不绑定requester参数,那么通过sender获得actor是tiopNode,而不是main。我们在testcase中做assertion的话,肯定就是错的。
另外,上例中用到了testActor,它是implicit变量,用于处理发送到main中的消息。主要是测试方便。
一个debug了5个小时的bug
case msg: Operation => pendingQueue = pendingQueue.enqueue(msg)
pendingQueue.enqueue(msg) 并不能更新pendingQueue自己,必须重新赋值才行。