Использование GPars для обработки узлов в дереве с кешированием обработанных узлов

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

Предположим, что я обрабатываю разные ветви дерева их собственными потоками.

Скажем, у узла есть имя. Когда я нажимаю на этот узел в первый раз, я хочу, чтобы он был обработан (что включает запись в базу данных и т. Д.) И добавлен в кеш (здесь просто простая карта).

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

Узел извлекается из базы данных, поэтому это не один и тот же объект в каждой ветке, я не думаю, что синхронизированные методы будут работать.

Я, конечно, хочу, чтобы другие неродственные узлы продолжали обрабатываться.

Например:

  • Поток 1 обрабатывает A-B-C-D
  • Поток 2 обрабатывает E-B-F-G
  • Поток 3 обрабатывает W-X-Y
  • Поток 4 обрабатывает L-M-N-O-P-Q-R-S-B-J

Допустим, поток 1 сначала попадает в узел B. Он обнаруживает, что его нет в кеше, и начинает его обрабатывать.

Приходит поток 2 и видит, что узла B нет в кеше, но над ним работают. Таким образом, он ожидает перехода к узлу F, пока поток 1 не завершит обработку узла B.

Поток 3 не заботится об узле B и поэтому продолжает работать.

Поток 4 появляется позже, когда поток 1 завершает работу с узлом B и находит узел B в кеше, поэтому он просто извлекает его из кеша и переходит к узлу J.

Я ищу предложения о том, как лучше всего применить GPAR в этой ситуации.

Спасибо!


person user1373467    schedule 08.11.2012    source источник


Ответы (1)


Моей первой мыслью было бы использовать общий ConcurrentHashmap с именами узлов в качестве ключей и DataflowVariables в качестве значений.

Потоки используют putIfAbsent (nodename, new DataflowVariable ()) для атомарной вставки обещания для данных обработанного узла, и если операция завершится успешно, поток может начать обработку узла и в конечном итоге привязать результат к DataflowVariable.

Если putIfAbsent () завершается неудачно, какой-то другой поток уже начал обработку узла с тем же именем. В этом случае текущий поток может просто захватить DataflowVarieble, связанный с именем узла, и дождаться, пока будет доступен результат - либо вызывая get (), либо whenBound ().

person Vaclav Pech    schedule 09.11.2012
comment
Итак, для потока 4, приведенного выше, должен ли он сначала выполнить get () на хэш-карте, а затем выполнить putIfAbsent ()? Или каждый поток должен всегда выполнять putIfAbsent, тогда, если элемент был обработан давно, DataFlowVariable всегда будет быстро возвращаться из get ()? Между прочим, я не могу проголосовать за ваш ответ (я пробовал) b / c у меня еще нет репутации 15 ;-) - person user1373467; 09.11.2012
comment
И глупый вопрос - я повторяю, используя eachParallel, могу ли я просто использовать DataFlowVariable напрямую или мне нужно использовать задачи DataFlow? Спасибо! - person user1373467; 09.11.2012
comment
Вы можете или не можете вызвать get () перед putIfAbsent (). Это может сэкономить некоторое время обработки, избегая создания DataflowVariables, которые никогда не будут использоваться, при условии, что вероятность того, что get () вернет ненулевое значение, высока. Однако, если get () возвращает null, никаких предположений делать не следует, и следует использовать putIfAbsent (), поскольку именно putIfAbsent () гарантирует атомарность. - person Vaclav Pech; 09.11.2012
comment
Как только значение было привязано к DataflowVariable, оно всегда будет немедленно возвращать это значение с помощью своего метода get (). - person Vaclav Pech; 09.11.2012
comment
DataflowVariables можно использовать из любых потоков независимо от того, были ли они запущены как простые потоки Java, потоки в составе пула, задачи GPars или методы параллельного сбора, такие как eachParallel. - person Vaclav Pech; 09.11.2012
comment
Отлично, спасибо! Мне очень нравятся GPAR (мы часто используем базовые * параллельные методы), но с точки зрения документации, похоже, вы уже понимаете некоторые вещи о параллелизме. Мне нужно руководство для новичков. Примеры: я понятия не имел, какую из многих стратегий здесь применить. Кроме того, что делает .join () при просмотре Dataflow? В любом случае, огромное спасибо за вашу помощь, а также за различные статьи и тому подобное, которые вы разместили в Интернете. - person user1373467; 09.11.2012
comment
Не за что. Спасибо за ответ. Я согласен с тем, что документация оставляет желать лучшего, и я верю, что по мере нашего продвижения она станет лучше. - person Vaclav Pech; 17.11.2012