Параллельная версия AtomicBoolean не работает, но версия агента работает

У меня есть метод, в котором я пытался распараллелить вычисление с помощью GPARS и вычислить совокупный логический результат «И» для вызовов. Этот метод обернут как @ActiveObject, который доставит результат в виде потока данных — в приведенном ниже коде используется исходный подход, когда я пытался сохранить агрегат, используя AtomicBoolean для его защиты.

Это не сработало (иногда мои тесты проходили, другие не проходили) на расчетной «конечной истине». Чтобы исправить это, я перешел с AtomicBoolean на подход Agent (boolean), и я думаю, что это «исправлено» - по крайней мере, мои тесты spock постоянно успешны.

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

Метод ниже - я поставил оригинальную версию, а исправленную версию ниже

@ActiveMethod
def evaluateAllAsync () {
    AtomicBoolean result = new AtomicBoolean(true)
    GParsPool.withPool {
        // do as parallel 
        conditions.eachParallel { condition ->
            println "evalAllAsync-parallel intermediate result start value is ${result.get()} and condition with expression ${condition.expression} evaluated to ${condition.evaluate()}"
            result.getAndSet(result.get() && condition.evaluate())
            println "recalc bool value is now ${result.get()}"
        }
    }
    println "evalAllAsync-parallel final result value is ${result.get()}"
    result.get()

}

Исправлена ​​проблема с использованием такой формы агента

@ActiveMethod
def evaluateAllAsync () {
    def result = new Agent (true)
    GParsPool.withPool {
        // do as parallel 
        conditions.eachParallel { condition ->
            println "evalAllAsync-parallel intermediate result start value is ${result.val} and condition with expression ${condition.expression} evaluated to ${condition.evaluate()}"
            result << { def res = it && condition.evaluate(); println "start> $it and finish> $res"; updateValue(res)}
            println "recalc bool value is now ${result.val}"
        }
    }
    println "evalAllAsync-parallel final result value is ${result.val}"
    result.val

}

Я поместил здесь debug println, чтобы видеть, что делает код.

Версия с агентом для защиты агрегированного значения bool работает.

Почему атомарное логическое значение не работает?


person WILLIAM WOODMAN    schedule 01.12.2016    source источник


Ответы (1)


Ну, это проблема в том, как вы используете AtomicBoolean. Вы всегда принудительно перезаписываете (getAndSet()) значение, хранящееся в нем, и игнорируете возможность того, что другие потоки могли изменить его, пока текущий поток занят «оценкой».

Возможно, вы хотели вместо этого использовать метод compareAndSet():

    def valueToUse = result.get()
    result.compareAndSet(valueToUse, valueToUse && condition.evaluate())
person Vaclav Pech    schedule 02.12.2016
comment
я думаю, это потому, что я предполагал, что сам getAndSet является атомарным и поэтому будет выполнять свое обновление без вмешательства других потоков. atomic.get Inside гарантировано - так что затем && с оценкой, затем установите это обратно, поэтому я думаю, что это мое предположение о том, как эти два будут работать (сначала оцените get&&eval - затем представьте это в getAndSet - предполагая, что следующий get увидит любые исправленные значения. - person WILLIAM WOODMAN; 02.12.2016
comment
однако это явно не то, что происходит - когда я смотрел, как моя консоль печатает все, что начинается, возвращает true, оценочное get && eval выводит true/false, как и ожидалось, но запланированное обновление и окончательный возврат иногда показывают true, иногда false - как это происходило при закрытии пула - я ожидал, что окончательный вариант увидит правильный окончательный расчет - person WILLIAM WOODMAN; 02.12.2016
comment
так что мое понимание неблокировки и того, как происходит состояние гонки, неверно - может быть, как вы говорите, я просто перезаписываю более раннее обновление, поэтому, если отправка обновления оказалась ложной, но была заменена 3-м обновлением вычисляется как истинное - тогда я Я просто перезаписываю - не контролируя, как следующий поток начинается с правильного значения. это говорит о том, что консоль говорит, что все они начинаются как истина. - person WILLIAM WOODMAN; 02.12.2016
comment
это подразумевает, что все они пошли и прочитали очень быстро - все видели одно и то же значение, но вычисление повторного вычисления требует времени - поэтому окончательные обновления просто случайным образом обновляют значение, а затем перезаписываются следующим потоком - поэтому они пропустили обновления от предыдущая ветка. я попытаюсь изменить образец, чтобы использовать сравнение и установить и вернуться - person WILLIAM WOODMAN; 02.12.2016
comment
думаю, что это, вероятно, описывает мое непонимание и предложенное вами решение. stackoverflow.com/questions/28147654 / - person WILLIAM WOODMAN; 02.12.2016