如何避免对 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
现在,您无需使用 actorOf2
或 actorOf
来生成此 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 查找结果证明太慢,创建一个记忆函数以保持对其他代码隐藏可变性将非常容易。
我正尝试在 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
现在,您无需使用 actorOf2
或 actorOf
来生成此 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 查找结果证明太慢,创建一个记忆函数以保持对其他代码隐藏可变性将非常容易。