Как сохранить состояние в 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 -> ()

person Iain    schedule 17.03.2015    source источник
comment
Не то, что вы ищете, но для агентов F # идиоматический подход заключается в используйте рекурсивный цикл для переноса состояния. Однако, если Akka.NET вынудит вас пойти по пути, ориентированному на мутации, может оказаться невозможным сделать это «хорошо». Можете ли вы реализовать актор для Akka.NET другими способами, кроме как на основе ReceiveActor? Что-то, что позволило бы вам использовать рекурсивный цикл опроса?   -  person Mark Seemann    schedule 17.03.2015
comment
Кстати, если есть другое идиоматическое решение, я просто не знаю об этом и тоже хотел бы получить образование :)   -  person Mark Seemann    schedule 17.03.2015


Ответы (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

person bruinbrown    schedule 17.03.2015

Есть два решения, в каждом из которых используется явное определение рекурсивного цикла, основная концепция акторов 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
person Bartosz Sypytkowski    schedule 17.03.2015