如何避免对Actor实例的可变引用

问题描述:

我正尝试在F#中获得一些Akka.NET参与者的一些经验。当一个演员需要将另一个演员作为自己的孩子时,我有这种情况。第一个演员会转换每条消息,然后将结果发送给其他演员。我使用actorOf2函数来派生演员。这里是我的代码:如何避免对Actor实例的可变引用

let actor1 work1 work2 = 
    let mutable actor2Ref = null 
    let imp (mailbox : Actor<'a>) msg = 
    let result = work1 msg 
    if actor2Ref = null then 
     actor2Ref <- spawn mailbox.Context "decide-actor" (actorOf2 <| work2) 
    actor2Ref <! result 
    imp 

let actor1Ref = actor1 work1' work2' 
    |> actorOf2 
    |> spawn system "my-actor" 

我不喜欢的是可变的演员参考。但是我必须让它变得可变,因为我需要mailbox.Context产生一个小孩演员,并且在第一次通话之前我没有任何背景。我看到this question,但我不知道如何将它应用于我的功能。

在更高级的场景中,我需要一个由键分区的子actor的集合。我在这种情况下使用了演员参考字典。有更好的(更多F#-ish)方法吗?

为了保持跨越迭代的“状态”,您需要明确地进行迭代。这样,你可以传递当前的“状态”作为尾调用参数。正如在这个问题您链接:

let actor1 work1 work2 (mailbox : Actor<'a>) = 
    let rec imp actor2 = 
    actor { 
     let! msg = mailbox.Receive() 
     let result = work1 msg 

     let actor2 = 
     match actor2 with 
     | Some a -> a // Already spawned on a previous iteration 
     | None -> spawn mailbox.Context "decide-actor" (actorOf2 <| work2) 

     actor2 <! result 
     return! imp (Some actor2) 
    } 

    imp None 

而现在,你不需要使用actorOf2actorOf产卵这个演员,因为它已经具有正确的签名:

let actor1Ref = 
    actor1 work1' work2' 
    |> spawn system "my-actor" 


编辑
如果你担心多余的样板,没有什么能阻止你从包装样板远作为函数(毕竟,actorOf2does something similar):

let actorOfWithState (f: Actor<'msg> -> 'state -> 'msg -> 'state) (initialState: 'state) mailbox = 
    let rec imp state = 
    actor { 
     let! msg = mailbox.Receive() 
     let newState = f mailbox state msg 
     return! imp newState 
    } 

    imp initialState 

然后:

let actor1 work1 work2 (mailbox : Actor<'a>) actor2 msg = 
    let result = work1 msg 
    let actor2 = 
    match actor2 with 
    | Some a -> a 
    | None -> spawn mailbox.Context "decide-actor" (actorOf2 work2) 

    actor2 <! result 
    actor2 

let actor1Ref = 
    actor1 work1' work2' 
    |> actorOfWithState 
    |> spawn system "my-actor" 
+0

是的,我有这个选项,但恕我直言,这个模板与明确的接收和演员工作流程是比我的功能更多的样板。而且更难以进行单元测试。 – Mikhail

+1

是的,更多的样板,但没有它,你不能保持状态。想一想:如果你的函数没有表示状态的任何参数,那么它所能做的就是依赖可变环境。 –

+0

为了回应您的问题,我已更新了答案。 –

你可以按照这些方法做一些事情,并且根本不存储对子actor的引用,因为Context已经为你做了这些。

let actor = 
    let ar = mailbox.Context.Child(actorName) 
    if ar.IsNobody() then 
     spawn mailbox.Context actorName handler 
    else ar 

如果Context.Child查找证明是太慢,造就了memoized功能,保持可变性与其他隐藏代码将是非常容易做到的。