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

Как я знаю, все операции в 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 прекращено в супервизоре актера.

Другие вопросы по тегам