There is a configured ActorSystem
with actors organized hierarchically as the following:
/user
/processes
/process1
/process2
/process3
To generate this scheme I use the next C# code:
// in entry point
IActorRef processesCoordinatorActorRef = ActorSystem.ActorOf(Props.Create<ProcessesCoordinatorActor>(), "processes");
// in ProcessesCoordinatorActor.cs:
IActorRef processOneActorRef = Context.ActorOf(Props.Create<ProccessActor>(), "process1");
IActorRef processTwoActorRef = Context.ActorOf(Props.Create<ProccessActor>(), "process2");
IActorRef processThreeActorRef = Context.ActorOf(Props.Create<ProccessActor>(), "process3");
My problem is that I want to add a child actor to process1
, process2
or process3
from the entry point code (outside of ProcessActor).
But now my hands are tied, because IActorRef
hides certain actor instance from me.
How do I solve it?
The child actor has to be created in the Context
of the parent actor. If you want to trigger that happening from outside, just send the parent a message and have it create the child actor (e.g. CreateChildActor
). There is no way to create an actor in one place, and then have it be "adopted" as the child of another actor.
Generally (and without knowing what you're actually trying to do), my hunch is that creating actors way down the hierarchy from your top-level / entry-point code is not a good direction for you to go down. You should let parent actors within the hierarchy do their job of creating and supervising children.
If you HAD to trigger this process from the entry point, you could send your CreateChildActor
to anywhere in the hierarchy using an ActorSelection
and/or resolve it to an IActorRef
.
Here's a code sample showing how you could do this, and have the subprocess notify the coordinator once it's ready (also here as a Fiddle):
using System;
using System.Threading;
using Akka.Actor;
namespace ProcessActors
{
class Program
{
/// <summary>
/// Top-level process coordinator actor.
/// </summary>
public class ProcessesCoordinatorActor : ReceiveActor
{
public ProcessesCoordinatorActor()
{
Receive<CreateChildActor>(createRequest =>
{
Console.WriteLine("{0} creating child actor: {1}", Self.Path, createRequest.ChildName);
var child = Context.ActorOf(createRequest.ChildProps, createRequest.ChildName);
if (createRequest.ActorToNotify != null)
createRequest.ActorToNotify.Tell(new ReadyForWork(child));
});
Receive<ReadyForWork>(ready =>
{
Console.WriteLine("Coordinator sees worker ready: {0}", ready.Worker.Path);
});
ReceiveAny(o =>
{
Console.WriteLine("{0} received {1}", Self.Path, o);
});
}
}
/// <summary>
/// Actor for a given process.
/// </summary>
public class ProcessActor : ReceiveActor
{
public ProcessActor()
{
Receive<CreateChildActor>(createRequest =>
{
Console.WriteLine("{0} creating child actor: {1}", Self.Path, createRequest.ChildName);
var child = Context.ActorOf(createRequest.ChildProps, createRequest.ChildName);
if (createRequest.ActorToNotify != null)
createRequest.ActorToNotify.Tell(new ReadyForWork(child));
});
ReceiveAny(o =>
{
Console.WriteLine("{0} received {1}", Self.Path, o);
});
}
}
/// <summary>
/// Sub-process.
/// </summary>
public class SubprocessActor : ReceiveActor
{
public SubprocessActor()
{
ReceiveAny(o =>
{
Console.WriteLine("{0} received {1}", Self.Path, o);
});
}
}
public static void Main(string[] args)
{
using (var system = ActorSystem.Create("MyActorSystem"))
{
Console.WriteLine("Starting up.");
var coordinator = system.ActorOf(Props.Create(() => new ProcessesCoordinatorActor()), "processes");
var processProps = Props.Create(() => new ProcessActor());
// create process actors
coordinator.Tell(new CreateNewProcess("process1", processProps));
coordinator.Tell(new CreateNewProcess("process2", processProps));
coordinator.Tell(new CreateNewProcess("process3", processProps));
var subprocessProps = Props.Create(() => new SubprocessActor());
// tiny sleep to let everything boot
Thread.Sleep(TimeSpan.FromMilliseconds(50));
// get handle to an actor somewhere down in the hierarchy
var process1 = system.ActorSelection("/user/processes/process1").ResolveOne(TimeSpan.FromSeconds(1)).Result;
// create subprocess of process1 and notify the coordinator of new subprocess actor
process1.Tell(new CreateNewSubprocess("subprocess1", subprocessProps, coordinator));
Console.ReadLine();
}
}
#region Messages
/// <summary>
/// Command to create ChildProps actor and notify another actor about it.
/// </summary>
public class CreateChildActor
{
public CreateChildActor(string childName, Props childProps, IActorRef actorToNotify)
{
ChildName = childName;
ActorToNotify = actorToNotify;
ChildProps = childProps;
}
public CreateChildActor(string childName, Props childProps)
: this(childName, childProps, null)
{
}
public Props ChildProps { get; private set; }
public string ChildName { get; private set; }
public IActorRef ActorToNotify { get; private set; }
}
public class CreateNewProcess : CreateChildActor
{
public CreateNewProcess(string childName, Props childProps, IActorRef actorToNotify)
: base(childName, childProps, actorToNotify)
{
}
public CreateNewProcess(string childName, Props childProps)
: this(childName, childProps, null)
{
}
}
public class CreateNewSubprocess : CreateChildActor
{
public CreateNewSubprocess(string childName, Props childProps, IActorRef actorToNotify)
: base(childName, childProps, actorToNotify)
{
}
public CreateNewSubprocess(string childName, Props childProps)
: this(childName, childProps, null)
{
}
}
/// <summary>
/// Report to another actor when ready.
/// </summary>
public class ReadyForWork
{
public ReadyForWork(IActorRef worker)
{
Worker = worker;
}
public IActorRef Worker { get; private set; }
}
#endregion
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With