简体   繁体   中英

Why isn't my actor receiving a message?

Issue:

I am struggling to understand why my Reporter actor is not receiving messages based on the following statement that's in my Generator actor:

reporter <! Message input

My reporter actor is the following:

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

Basically, a console is launched that accepts input from the user. My Generator actor forwards that input to my Reporter actor. However, the code above never gets executed.

The code is the following:

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

Update:

I learned that I could trigger my Reporter actor by replacing the mailbox parameter with an arbitrary message parameter:

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

I still don't understand when I should use a mailbox parameter versus when I should rely on a message parameter.

The difference is in how actorOf and actorOf2 work.

actorOf in conjunction with spawn creates an actor as a child of the root of the system which will handle messages with the function 'Message -> unit that was passed to it.

actorOf2 in in conjunction with spawn creates an actor as a child of the actor that you passed in and the child will handle the messages with the function 'Message -> unit that was passed.

Your original function signature for reporter actor was:

Actor<'Message> -> unit

and you used spawn system "reporterActor" (actorOf(reporterActor))

In this case you were saying that the message type that the new actor that was created would receive would be of type Actor<'Message> . This compiled because actorof just expects a function that takes a 'Message, and a 'Message is generic therefore Actor<'Message> satisfied the 'Message parameter.

When you updated the signature of reporterActor you change the signature to 'Message -> unit which is what actorOf is actually intended to accept.

In short generics allowed your code to compile since 'Message isn't really restricted, nor should it really be.

From: http://getakka.net/docs/FSharp%20API

actorOf (fn : 'Message -> unit) (mailbox : Actor<'Message>) : Cont<'Message, 'Returned> - uses a function, which takes a message as the only parameter. Mailbox parameter is injected by spawning functions.

actorOf2 (fn : Actor<'Message> -> 'Message -> unit) (mailbox : Actor<'Message>) : Cont<'Message, 'Returned> - uses a function, which takes both the message and an Actor instance as the parameters. Mailbox parameter is injected by spawning functions. Example:

 > 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 - spawns an actor using a specified actor computation expression. The actor can only be used locally.

All of these functions may be used with either the actor system or the actor itself. In the first case the spawned actor will be placed under /user root guardian of the current actor system hierarchy. In the second option the spawned actor will become a child of the actor used as the actorFactory parameter of the spawning function.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM