Как сохранить состояние в F# Akka.NET Actor?

В C# ReceiveActors Я могу просто указать состояние как частные поля в классе. Как я должен сделать это идиоматическим способом с 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
Другие вопросы по тегам