Как сохранить состояние в F# Akka.NET Actor?
В C# ReceiveActor
s Я могу просто указать состояние как частные поля в классе. Как я должен сделать это идиоматическим способом с F# API?
Это хорошая идея? Есть альтернативы?
let handleMessage (mailbox: Actor<'a>) msg =
let mutable i = 1
match msg with
| Some x -> i <- i + x
| None -> ()
2 ответа
То, как вы предлагаете, совершенно уместно в качестве средства хранения состояния внутри актера. Ограничения одновременности обработки только одного сообщения в любое время означают, что невозможно попасть в недопустимые состояния в результате конфликта в расположении общей памяти.
Однако это не самый идиоматический вариант. Akka.Net предоставляет F# API для работы с актерами аналогично F# MailboxProcessors. В этом случае вы определяете своего актера как хвостовую рекурсивную функцию, которая вызывает себя с каким-то новым состоянием. Вот пример
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
Для полной документации по API Akka.Net F# см. Http://getakka.net/wiki/FSharp%20API
Есть два решения, оба они используют явное определение рекурсивного цикла, основная концепция акторов Akka F#.
Сначала вы можете определить переменные, которые должны быть видны только внутри области действия актера, перед определением цикла (в примере ниже я изменил 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