NullPointerException в GPars, Актер
Groovy: 1,8,6
GPars: 0,12 или 1,2,1
ОС: Ubuntu 14.04 LTS
@Grab(group='org.codehaus.gpars', module='gpars', version='0.12')
import groovyx.gpars.actor.Actor
import groovyx.gpars.actor.Actors
def a = 1
def b = 100000
def reactor2 = Actors.reactor {
println " $it"
}
def reactor1 = Actors.reactor {
println "$it"
reactor2 << it
}
Actor actor = Actors.actor {
(a..b).each {reactor1 << it}
}
actor.join()
reactor1.stop()
reactor1.join()
reactor2.stop()
reactor2.join()
Когда этот код выполняется, NullPointerException встречается часто. Чем шире и шире диапазон значений a и b, тем легче и легче возникает эта ошибка. Но ошибка никогда не возникает, когда диапазон ограничен.
Я не могу понять, почему произошла ошибка.
1 ответ
Из приведенного вами примера кода я не уверен на 100%, чего вы пытаетесь достичь. Похоже, вы пытаетесь Actor
для каждого значения в пределах предоставленного диапазона и отреагируйте на сообщение, распечатав предоставленное значение.
Учитывая это, у вашего кода есть некоторые проблемы. Основной проблемой является отсутствие loop{}
закрытие, которое страхует Actor
ожидает следующего входящего сообщения после его обработки.
Во-вторых, ссылаясь stop()
тоже не помогает Это просто останавливает Actor
от получения дополнительных сообщений. В вашем случае это не повредит, так как вы немедленно вызываете join, но добавляет путаницы.
Чтобы это работало, вот ваш код, упрощенный до рабочего примера:
import groovyx.gpars.actor.Actors
def a = 1
def b = 100000
def actor = Actors.actor {
loop {
react {
println it
}
}
}
(a..b).each {
actor << it
}
actor.join()
В этом примере для каждого значения в диапазоне к субъекту добавляется сообщение, которое реагирует на сообщение, печатая значение, а затем, из-за закрытия цикла, ожидает следующего входящего сообщения.
Так что этот пример должен выполнить то, что вы хотите. Однако, чтобы дать некоторую ясность в том, что происходит в вашем коде, вот объяснение.
Когда ваш Actor
"реагирует" на сообщение, вы отправляете сообщение Reactor
, Даже если вы явно не возвращаете значение, в Groovy последняя строка в Closure - это оператор return. В результате, поскольку ваша последняя строка в закрытии println
, возврат этого равен нулю. Так что ваши Reactor
возвращает ноль, что рассматривается как сообщение Actor
и снова обработан.
Чтобы избежать этого сценария, вам необходимо оценить возвращаемое сообщение и делегировать сообщение или распечатать его, только если оно еще не было обработано. Я обновил ваш код и намеренно вернул "готово" в качестве сообщения для ясности. Вы можете изменить код, чтобы просто проверить null
сообщение перед его обработкой:
импорт groovyx.gpars.actor.Actors
def a = 1
def b = 100000
def reactor2 = Actors.reactor { message ->
if(!message.equals("done")) {
println "\t\tReact Again: $message"
}
return "done"
}
def reactor = Actors.reactor { message ->
if(!message.equals("done")) {
println "\tReact: $message"
reactor2 << message
}
return "done"
}
def actor = Actors.actor {
loop {
react {
if(!it.equals("done")) {
println it
reactor << it
}
}
}
}
(a..b).each {
actor << it
// actor.oi
}
actor.join()