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

How to avoid mutable reference to Actor instance

我正尝试在 F# 中获得一些使用 Akka.NET actor 的经验。我有一个场景,当一个演员需要旋转另一个演员作为它的 child。第一个演员将转换每条消息,然后将结果发送给另一个演员。我使用 actorOf2 函数生成演员。这是我的代码:

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 来生成 child 演员,而且在第一次调用之前我没有任何上下文。我看到了 但我不知道如何将它应用到我的函数中。

在更高级的场景中,我需要 child 个按键分区的演员集合。在这种情况下,我使用的是演员参考词典。有更好的(更像 F# 的)方法吗?

为了让您的 "state" 跨迭代,您需要明确迭代。这样,您可以将当前 "state" 作为尾调用参数传递。就像您链接的问题一样:

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 来生成此 actor,因为它已经具有正确的签名:

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

.
编辑
如果您担心额外的样板文件,没有什么能阻止您将样板文件作为函数打包(毕竟,actorOf2 does 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"

您可以按照这些思路做一些事情,只是根本不存储对子 actor 的引用,因为 Context 已经为您做了。

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

如果 Context.Child 查找结果证明太慢,创建一个记忆函数以保持对其他代码隐藏可变性将非常容易。