Есть ли способ подождать полной остановки актера?
Как я знаю, все операции в Akka.Net являются асинхронными, и Context.Stop()
просто отправляет Stop
сообщение актеру. Это означает, что актер будет жив в течение некоторого времени, прежде чем он полностью отключится.
И если я позвоню Context.Child()
сразу после Context.Stop()
с именем актера я только что остановился, я получу того же актера.
Вот пример кода
var actor = context.Child(actorName);
if (actor.Equals(ActorRefs.Nobody))
{
actor = CreateNewActor();
}
Context.Stop(actor)
actor = context.Child(actorName);
// what do we get here, same actor or ActorRefs.Nobody ?
Мое приложение создает акторы для обработки событий из терминалов. Каждый раз, когда подключается новый терминал, я создаю нового актера, вызывая Context.Child()
используя имя терминала. Когда терминал отключается, я останавливаю актера.
Проблема в том, что иногда я получаю сообщение Connect сразу после Disconnect для того же терминала, и в результате я получаю актера, который будет остановлен. Есть ли способ проверить, что актер получил сообщение Стоп и скоро будет остановлен?
2 ответа
Я решил закончить с обработкой Terminated
Сообщения.
После получения Disconnect
сообщение и остановка актера, я держу его имя в ActorsToBeStopped
HashSet и перед созданием нового актера при получении Connect
сообщение, я проверяю, существует ли он там. Если это так, я держу это Connect
сообщение в словаре с именем актера в качестве ключа и Connect
сообщение в качестве значения и обработайте его после получения прекращенного сообщения для соответствующего субъекта.
Что-то вроде этого:
private readonly Dictionary<string, Connect> postponedConnectMessages = new Dictionary<string, Connect>();
private readonly HashSet<string> actorsToBeStopped = new HashSet<string>();
// ...
Receive<Disconnected>(t =>
{
var actor = GetActorByName(t.Name);
Context.Stop(actor);
actorsToBeStopped.Add(actor.Path.Name);
});
Receive<Connected>(t =>
{
var actor = GetActorByName(t.Name);
if (actorsToBeStopped.Contains(actor.Path.Name))
{
postponedConnectMessages[actor.Path.Name] = t;
return;
}
// work with actor
}
Receive<Terminated>(t =>
{
var deadActorName = t.ActorRef.Path.Name;
actorsToBeStopped.Remove(deadActorName);
if (postponedConnectMessages.ContainsKey(deadActorName))
{
var connectMessage = postponedConnectMessages[deadActorName];
postponedConnectMessages.Remove(deadActorName);
var actor = GetActorByName(connectMessage.Name);
// we sure we have new actor here
// work with actor
}
}
РЕДАКТИРОВАТЬ
Печально, что я не могу написать тест, потому что Akka.TestKit
не позволяет мне создавать TestActor
с тем же именем даже после того, как он был остановлен:
public void StopTest()
{
var t = CreateTestActor("AAAA");
Watch(t);
Sys.Stop(t);
ExpectTerminated(t, TimeSpan.FromSeconds(10));
var t2 = CreateTestActor("AAAA"); // test fails here
}
Или, может быть, это не было остановлено после ExpectTerminated
, но в любом случае я не знаю, как ждать его окончания.
Ты можешь использовать
var shutdown = actor.GracefulStop(TimeSpan.FromSeconds(42));
Возвращает задание, результат которого подтверждает отключение в течение 42 секунд.
ОБНОВИТЬ
Но, в случае, если вы хотите позже воссоздать актера с тем же именем, вы должны прослушать сообщение Ter прекращено в супервизоре актера.