Darkstarone
Darkstarone

Reputation: 4730

F# Akka - Wait for one group of actors to finish before starting next group

So I have a toy example here, where I create 200 actors and then send the first 100 a "first" message, before sending the last 100 a "second" message.

open System
open Akka.Actor
open Akka.Configuration
open Akka.FSharp

let system = System.create "test" (Configuration.defaultConfig())

let myActor (mailbox: Actor<_>) = 
    actor {
        let rand = System.Random()
        let! message = mailbox.Receive ()
        match message with
        | "first" -> printfn "first group"
        | _ -> printfn "second group"
        Thread.SpinWait (rand.Next(100,1000))
    }
let actorArray = Array.create 200 (spawn system "myActor" myActor)

{0..199} |> Seq.iter (fun a ->
    actorArray.[a] <- spawn system (string a) myActor
)

// First group
{0..100} |> Seq.iter(fun a ->
    actorArray.[a] <! "first"
    ()
)
// Second group
{101..199} |> Seq.iter(fun a ->
    actorArray.[a] <! "second"
    ()
)

What I'd like is for the first 100 hundred actors to complete (i.e. to print and terminate) before sending messages to the second group, which does not happen.

I've started looking at Akka's F# monitoring module, but I'm not exactly sure how to implement it.

Upvotes: 1

Views: 496

Answers (1)

Darkstarone
Darkstarone

Reputation: 4730

So I've created a solution, not sure if it's the most idiomatic, but it does the job!

open System
open Akka.Actor
open Akka.Configuration
open Akka.FSharp

let system = System.create "MySystem" (Configuration.defaultConfig())

let myActor (mailbox: Actor<_>) = 
    actor {
        let rand = System.Random()
        let! message = mailbox.Receive()
        let sender = mailbox.Sender()
        match message with
        | "first" -> printfn "first group"    
        | _ -> printfn "second group"
        Thread.SpinWait (rand.Next(100,1000))
        sender <! "Done"
    }

let myMonitor (mailbox: Actor<_>) =
    let mutable i = 99
    let actorArray = Array.create 200 (spawn system "myActor" myActor)
    {0..199} |> Seq.iter (fun a ->
        actorArray.[a] <- spawn system (string a) myActor
        ()
    )
    // First group
    {0..100} |> Seq.iter(fun a ->
        actorArray.[a] <! "first"
        ()
    )
    let rec loop() =
        actor {
            let! message = mailbox.Receive()
            match message with
            | _ -> 
                i <- (i - 1)
                if (i = 0) then
                    // Second group
                    {101..199} |> Seq.iter(fun a ->
                        actorArray.[a] <! "second"
                        ()
                    )
            return! loop()
        } 
    loop()

let mon = spawn system "myMon" myMonitor

In essence what happens is an external actor myMonitor sets up the environment and begins the first set of tasks outside its recursive loop. The actors on task now send "Done" when they complete, and this is processed inside the myMonitor recursive loop.

Once myMonitor has received all messages from the first block, it starts the second.

Upvotes: 1

Related Questions