Использование будущего обратного вызова внутри аккера akka

Я нашел в документах Akka:

При использовании будущих обратных вызовов, таких как onComplete, onSuccess и onFailure, внутри участников необходимо тщательно избегать закрытия ссылки на содержащихся участников, т.е. не вызывать методы или доступ к изменяемому состоянию на окружающем акторе из обратного вызова.

Значит ли это, что я всегда должен использовать future pipeTo self, а затем вызывать некоторые функции? Или я все еще могу использовать обратные вызовы с помощью метода, тогда как мне избежать ошибок concurrency?

Ответ 1

Это означает:

class NotThreadSafeActor extends Actor {

  import context.dispatcher

  var counter = 0

  def receive = {
    case any =>
      counter = counter + 1
      Future {
        // do something else on a future
        Thread.sleep(2000)
      }.onComplete {
        _ => counter = counter + 1
      }
  }
}

В этом примере как метод приема актера, так и Future onComplete изменяют изменяемую переменную counter. В этом примере игрушек его легче увидеть, но вызов Future может быть вложенными методами, которые одинаково захватывают изменяемую переменную.

Проблема заключается в том, что вызов onComplete может выполняться в другом потоке для самого актера, поэтому вполне возможно, чтобы один поток выполнял receive, а другой выполнял onComplete, тем самым давая вам условие гонки. Это в первую очередь отрицает точку актера.

Ответ 2

Да, вы должны отправить сообщение окружающему актеру, если обратный вызов изменяет внутреннее состояние актера. Это самый простой (и предпочтительный) способ избежать рас.

Ответ 3

Думаю, я был бы упущен, если бы не упомянул здесь, что сделал небольшую утилиту для обхода этого ограничения. Другими словами, мой ответ на ваш вопрос "Нет", вы не должны использовать такое неудобное обходное решение, вы должны использовать https://github.com/makoConstruct/RequestResponseActor

как это работает?

В основном, за фьючерсами и promises он передает каждый запрос в Request(id:Int, content:Any), а когда он получает Response(id, result), он завершает будущее, соответствующее id со значением result. Он также способен передавать сбои, и, насколько я могу судить, akka может регистрировать таймауты запроса. RequestResponseActor предоставляет специальный неявный контекст выполнения для применения к обратным вызовам, связанным с фьючерсами, ожидающими сообщения Response. Этот тупой контекст выполнения гарантирует, что они выполняются во время обработки сообщения Response, тем самым гарантируя, что у Актера будет эксклюзивный доступ к его состоянию, когда будут срабатывать будущие обратные вызовы.

Ответ 4

Может быть, это может помочь. Это эксперимент, который я сделал, и тест довольно убедительный... однако, это еще эксперимент, поэтому не принимайте это как опыт.

https://github.com/Adeynack/ScalaLearning/tree/master/ActorThreadingTest/src/main/scala/david/ActorThreadingTest

Откройте для комментариев или предложений, конечно.

Фьючерсы с актерами - предмет, который меня очень интересует.