Почему мой актер не получает сообщения?

Проблема:

Я изо всех сил пытаюсь понять, почему мой актер Reporter не получает сообщения на основе следующего утверждения, которое находится в моем актере Generator:

reporter <! Message input

Актер моего репортера:

let reporterActor (mailbox:Actor<_>) =

    let rec loop() = actor { let! msg = mailbox.Receive()
                             match msg |> box :?> Command with
                             | Start     -> ()
                             | Message v -> printf "%s" v
                             | Exit      -> mailbox.Context.System.Terminate() |> ignore }
    loop() |> ignore

По сути, запускается консоль, которая принимает ввод от пользователя. Мой актор Generator перенаправляет этот ввод моему актору Reporter. Однако приведенный выше код никогда не выполняется.

Код следующий:

module Main

open System
open Akka.FSharp
open Akka.Actor
open Actors

type Command = 
    | Message of string
    | Start | Exit

let reporterActor (mailbox:Actor<_>) =

    let rec loop() = actor { let! msg = mailbox.Receive()
                             match msg |> box :?> Command with
                             | Start     -> ()
                             | Message v -> printf "%s" v
                             | Exit      -> mailbox.Context.System.Terminate() |> ignore }
    loop() |> ignore


let generatorActor (reporter:IActorRef) (mailbox:Actor<_>) message =

    let handle input = match input with
                       | "exit" -> mailbox.Context.System.Terminate |> ignore
                       | _      -> reporter <! Message input

    handle (Console.ReadLine().ToLower())

[<EntryPoint>]
let main argv = 
    let system =         System.create "system"         (Configuration.load())
    let reporterActor =  spawn system  "reporterActor"  (actorOf(reporterActor))
    let generatorActor = spawn system  "generatorActor" (actorOf2(generatorActor reporterActor))

    generatorActor <! Start
    system.AwaitTermination ()
    0

Обновление:

Я узнал, что могу активировать актор Reporter, заменив параметр почтового ящика произвольным параметром сообщения:

let reporterActor message =
     match message |> box :?> Command with
     | Start     -> ()
     | Message v -> printf "Reporting: %s" v
     | Exit      -> failwith "Kill this!"

Я до сих пор не понимаю, когда следует использовать параметр почтового ящика, а когда полагаться на параметр сообщения.


person Scott Nimrod    schedule 09.02.2017    source источник


Ответы (1)


Разница заключается в том, как работают ActorOf и ActorOf2.

actOf в сочетании с spawn создает актор как дочерний элемент корня системы, который будет обрабатывать сообщения с переданной ему функцией 'Message -> unit.

actOf2 в сочетании с spawn создает актор как дочерний по отношению к актору, который вы передали, и дочерний элемент будет обрабатывать сообщения с переданной функцией 'Message -> unit.

Ваша исходная сигнатура функции для актера-репортера была:

Actor<'Message> -> unit

и вы использовали spawn system "reporterActor" (actorOf(reporterActor))

В этом случае вы говорили, что тип сообщения, которое получит новый созданный актор, будет иметь тип Actor<'Message>. Это скомпилировано, потому что actof просто ожидает функцию, которая принимает «Сообщение», а «Сообщение» является общим, поэтому Actor<'Message> удовлетворяет параметру «Сообщение».

Когда вы обновляете подпись reporterActor, вы меняете подпись на 'Message -> unit, которую на самом деле должен принимать actOf.

Короче говоря, дженерики позволили вашему коду скомпилироваться, поскольку «Сообщение на самом деле не ограничено и не должно быть».

Источник: http://getakka.net/docs/FSharp%20API

actOf (fn : 'Message -> unit) (mailbox : Actor<'Message>) : Cont<'Message, 'Returned> - использует функцию, которая принимает сообщение в качестве единственного параметра. Параметр почтового ящика вводится порождающими функциями.

actorOf2 (fn : Actor<'Message> -> 'Message -> unit) (mailbox : Actor<'Message>) : Cont<'Message, 'Returned> - использует функцию, которая принимает в качестве параметров как сообщение, так и экземпляр Актера. Параметр почтового ящика вводится порождающими функциями. Пример:

> let handleMessage (mailbox: Actor<'a>) msg =
>     match msg with
>     | Some x -> printf "%A" x
>     | None -> ()
> 
> let aref = spawn system "my-actor" (actorOf2 handleMessage) let
> blackHole = spawn system "black-hole" (actorOf (fun msg -> ()))

spawn (actorFactory : IActorRefFactory) (name : string) (f : Actor‹'Message> -> Cont‹'Message, 'Returned>) : IActorRef — порождает актера, используя указанное выражение вычисления актера. Актер может использоваться только локально.

Все эти функции могут использоваться как с системой акторов, так и с самим актором. В первом случае порожденный актор будет помещен в /user корневого опекуна текущей системной иерархии акторов. Во втором варианте порожденный актор станет дочерним по отношению к актору, используемому в качестве параметраactorFactory функции порождения.

person awright18    schedule 10.02.2017
comment
Спасибо Адам. Есть ли у вас опыт тестирования актеров? - person Scott Nimrod; 10.02.2017
comment
Я не знаю, но я нашел эту ссылку petabridge .com/blog/ это может быть полезно при тестировании. Я не уверен, есть ли для этого F#-ified библиотека или нет, но я уверен, что вы могли бы заставить ее работать с F#. - person awright18; 12.02.2017