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

10

在 F# API 中该如何用一种惯用的方式来处理状态,使其成为类中的私有字段?与 C# 的 ReceiveActor 不同。

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

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

不是你想要的,但对于[F#代理],惯用的方法是使用递归循环来传递状态。然而,如果Akka.NET迫使您走向变异导向的道路,可能无法以“好的”方式完成。您能否以其他方式实现Akka.NET的Actor,而不是从ReceiveActor派生?有什么东西可以让您使用递归轮询循环? - Mark Seemann
1
顺便说一句,如果还有其他惯用的解决方案,我只是不知道而已,也希望能得到教育 :) - Mark Seemann
2个回答

20
你提出的方式是完全合适的,作为在actor内部存储状态的一种方法。每次只处理一个消息的并发限制意味着不可能因为共享内存位置上的争用而进入无效状态。
然而,这并不是最常用的选项。Akka.Net提供了一个F# API,以类似于F# MailboxProcessors的方式与actors一起工作。在这种情况下,你将其定义为一个尾递归函数,该函数使用一些新状态调用自身。以下是一个示例。
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


10

有两种解决方案,它们都使用显式递归循环定义,这是Akka F# actor的主要概念。

首先,您可以在循环定义之前定义变量,这些变量应该仅在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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接