Есть ли способ дождаться полной остановки актера?

Насколько я знаю, все операции в 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 для того же терминала, и в результате я получаю актор, который будет остановлен. Есть ли способ проверить, что актор получил стоп-сообщение и скоро будет остановлен?


person bonzaster    schedule 06.01.2017    source источник
comment
Вам следует прочитать о: Мониторинге жизненного цикла, также известном как DeathWatch. Отключении с подтверждением: GracefulStop.   -  person Igor Berman    schedule 07.01.2017
comment
Одно из предложений, которое приходит мне на ум, это также FSM. После того, как актер получает сообщение об отключении, вы переключаетесь в новое состояние и меняете поведение. Поэтому сообщение о подключении не будет обрабатываться обычным образом. И вы также можете определить, что должен делать актор, когда сообщение о подключении не обрабатывается.   -  person Branislav Lazic    schedule 08.01.2017


Ответы (2)


Ты можешь использовать

var shutdown = актер.GracefulStop(TimeSpan.FromSeconds(42));

Возвращает задачу, результат которой подтверждает завершение работы в течение 42 секунд.

ОБНОВЛЕНИЕ

Но, в случае, если вы хотите позже воссоздать актера с тем же именем, вы должны прослушать сообщение «Завершено» в супервизоре актера.

person Yury Glushkov    schedule 07.01.2017
comment
Насколько я понимаю, примечание здесь мне не поможет: Предупреждение Имейте в виду, что остановка актора и отмена регистрации его имени — это отдельные события, происходящие асинхронно друг от друга. Поэтому может случиться так, что вы обнаружите, что это имя все еще используется после возврата GracefulStop(). Чтобы гарантировать надлежащую отмену регистрации, повторно используйте имена только от супервайзера, которым вы управляете, и только в ответ на сообщение о прекращении, т. е. не для субъектов верхнего уровня. - person bonzaster; 09.01.2017
comment
Ой, извини. Я неправильно понял ваш первоначальный вопрос. Да, Grasfull top не гарантирует, что имя актера снято с регистрации. В вашем случае вы должны слушать Terminated сообщения в актере, который контролирует ваших актеров с именами терминалов. - person Yury Glushkov; 09.01.2017

Я решил закончить с обработкой Terminated сообщений.

После получения сообщения Disconnect и остановки актора я сохраняю его имя в ActorsToBeStopped HashSet, а перед созданием нового актора при получении сообщения Connect проверяю, существует ли он там. Если это так, я сохраняю это сообщение Connect в словаре с именем актера в качестве ключа и сообщением Connect в качестве значения и обрабатываю его после получения сообщения Terminated для соответствующего актера.

Что-то вроде этого:

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, но все равно я не знаю, как дождаться его окончания.

person bonzaster    schedule 09.01.2017