如何在 F# Akka.NET Actor 中存储状态?

How to store state in an F# Akka.NET Actor?

在 C# ReceiveActors 中,我可以在 class 中将状态作为私有字段。我应该如何以惯用的方式使用 F# API?

这是个好主意吗?还有其他选择吗?

let handleMessage (mailbox: Actor<'a>) msg =
    let mutable i = 1
    match msg with
    | Some x -> i <- i + x
    | None -> ()

您提出的方法作为在 actor 中存储状态的方法是完全合适的。任何时候只处理 1 条消息的并发约束意味着不可能由于共享内存位置的争用而进入无效状态。

但是,这不是最惯用的选项。 Akka.Net 提供 F# API 以类似于 F# MailboxProcessors 的方式与参与者一起工作。在这种情况下,您将您的 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

有两种解决方案,它们都使用显式递归循环定义,这是 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