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

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

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

13
user1078671 28 Май 2014 в 13:45

4 ответа

Лучший ответ

Это значит это:

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
      }
  }
}

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

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

12
sksamuel 28 Май 2014 в 13:58
Я не заинтересован в доступе/изменении переменных в качестве методов вызова из обратного вызова. В каких случаях небезопасно вызывать метод из обратного вызова (если он ничего не мутирует, просто выполняет некоторую логику)?
 – 
user1078671
28 Май 2014 в 14:05
Изменяет ли другой Actor свое внутреннее состояние?
 – 
goral
28 Май 2014 в 14:11
1
Для обратного вызова совершенно безопасно вызывать методы, которые не имеют побочных эффектов (состояние мутации). Если вы хотите изменить состояние, пусть ваш onComplete / onSuccess и т. д. отправит сообщение актеру, чтобы он правильно поставился в очередь. @goral не уверен, что ты спрашиваешь
 – 
sksamuel
28 Май 2014 в 14:26
Если, например, какой-то другой Actor изменит свое внутреннее состояние, то если мы запускаем его в разных потоках, то эта логика может быть неверной (если она зависит от этого состояния). Но если задача Actor чиста с точки зрения выполняемых функций, я бы также сказал, что она безопасна.
 – 
goral
28 Май 2014 в 14:42
В приведенном выше примере следует ли нам использовать Await.result и закрепленный диспетчер, если мы хотим убедиться, что счетчик обновляется до того, как следующее сообщение будет обработано receive? предположим, что перед блоком Future {} мы получаем состояние счетчика. Если мы используем приоритетный почтовый ящик с шаблоном result pipeTo self, кажется, что счетчик будет непротиворечивым только в конечном итоге?
 – 
Matt
1 Авг 2016 в 01:47

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

4
drexin 28 Май 2014 в 13:54
Но если не изменяет какое-либо состояние, а просто вызывает некоторые методы, которые также могут возвращать будущее с обратным вызовом (вложенные обратные вызовы)?
 – 
user1078671
28 Май 2014 в 13:57

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

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

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

0
Community 20 Июн 2020 в 12:12

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

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

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

Будущее с актерами - тема, которая меня очень интересует.

0
Adeynack 15 Июн 2015 в 19:28