在C#ReceiveActor
中,我可以将类状态作为私有字段。我应该如何使用F#API以惯用的方式执行此操作?
这是个好主意吗?任何替代方案?
let handleMessage (mailbox: Actor<'a>) msg =
let mutable i = 1
match msg with
| Some x -> i <- i + x
| None -> ()
答案 0 :(得分:18)
你提出的方式完全适合作为在演员中存储国家的手段。任何时候只处理1条消息的并发约束意味着由于共享内存位置的争用而无法进入无效状态。
然而,这不是最惯用的选择。 Akka.Net提供了一个F#API,以与F#MailboxProcessors类似的方式与actor一起工作。在这种情况下,您将actor定义为尾递归函数,该函数使用一些新状态调用自身。这是一个例子
spawn system "hello" <|
fun mailbox ->
let rec loop state =
actor {
let! msg = mailbox.Receive ()
printfn "Received %A. Now received %s messages" msg state
return! loop (state + 1) //Increment a counter for the number of times the actor has received a message
}
loop 0
有关Akka.Net F#API的完整文档,请参阅http://getakka.net/wiki/FSharp%20API
答案 1 :(得分:8)
有两个解决方案,它们都使用显式递归循环定义,Akka F#actors的主要概念。
首先,您可以在循环定义之前定义变量,这些变量应仅在actor的范围内可见(在下面的示例中,我将i
定义更改为引用单元格,因为可变变量不能是被闭包捕获):
let actorRef =
spawn system "my-actor" <| fun mailbox ->
let i = ref 1
let rec loop () =
actor {
let! msg = mailbox.Receive()
match msg with
| Some x -> i := !i + x
| None -> ()
return! loop()
}
loop()
然而,更多建议的解决方案是在消息处理期间保持状态不可变,并且仅在传入下一个循环调用时更改它,就像这样:
let actorRef =
spawn system "my-actor" <| fun mailbox ->
let rec loop i =
actor {
let! msg = mailbox.Receive()
match msg with
| Some x -> return! loop (i + x)
| None -> return! loop i
}
loop 1 // invoke first call with initial state