TPL Dataflow, как пересылать элементы только одному конкретному целевому блоку из многих связанных целевых блоков?

18

Я ищу решение блока потока данных TPL, которое может содержать больше одного элемента, который может связываться с несколькими целевыми блоками, но который имеет возможность пересылать элемент только определенному целевому блоку, который передает фильтр / сказуемое. Ни в коем случае предмет должен быть доставлен одновременно на несколько целевых блоков, всегда только на тот, который соответствует фильтру или элементу, может быть отброшен. Я не люблю BroadCastBlock, потому что, если я правильно понимаю, он не гарантирует доставку (или делает это?), И фильтрация выполняется на стороне целевого блока, то есть BroadCastBlock по существу отправляет копии каждого элемента всем связанным целевым блокам. Он также не может содержать более одного элемента в любое время, если я правильно понимаю. Я не хочу использовать Post / Async, но поддерживаю цепочку LinkTo.

Есть ли способ создать полный блок потока данных? Или я не понимаю, как работает BroadCastBlock? К сожалению, на самом деле не так много документации, которая детализируется и охватывает варианты использования. Любые идеи высоко ценятся.     

задан Matt Wolf 28.11.2012 в 07:35
источник

2 ответа

22

Если я правильно вас понимаю, то, что вы хотите, может быть достигнуто простым BufferBlock , которое будет связано со всеми вашими целевыми блоками с предикатами. Вы также (безоговорочно) свяжете его с NullTarget block , чтобы отбросить элементы, которые не совпадали.

Что-то вроде:

var forwarder = new BufferBlock<SomeType>();
forwarder.LinkTo(target1, item => matchesTarget1(item));
forwarder.LinkTo(target2, item => matchesTarget2(item));
forwarder.LinkTo(DataflowBlock.NullTarget<SomeType>());

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

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

С BroadcastBlock элементы могут быть удалены, если ни один блок не принимает их (даже если они могут принять его позже). Но он не отбрасывает предметы наугад, поэтому, если у ваших целевых блоков не установлено BoundedCapacity , я думаю, вы можете быть уверены, что они получат все предметы, которые они не снижают (например, используя предикат в LinkTo() ).     

ответ дан svick 28.11.2012 в 17:10
  • Как это технически работает? Проводится ли каждая цель до тех пор, пока не будет найдено совпадение, и если она не будет забита памятью, если не будет сброшена нулевым целевым блоком? И выполняет ли какой-то заказ целевой блок, который я связываю сначала, затем следующий ... вопрос? –  Matt Wolf 28.11.2012 в 17:17
  • Да, каждая цель проверяется последовательно. Если цель не совпадает, элемент останется в буферном блоке. И в этом случае засорение памяти не вызывает большой озабоченности, забивая трубопровод. Другими словами, это означало бы, что никакой другой элемент не будет отправлен из этого блока, пока проблема не будет принята какой-либо целью. Вот почему необходим блок NullTarget. И да, порядок имеет значение, и именно поэтому вы можете указать, хотите ли вы добавить или добавить каждую цель в список. –  svick 28.11.2012 в 17:23
  • Вы действительно парень, чтобы пойти в TDF. Потрясающие. Большое спасибо. Вы широко используете поток данных TPL или почему глубокие знания (среди других тем) этой библиотеки? Вы упомянули ранее, что вы не связаны с командой параллелизма MS. –  Matt Wolf 28.11.2012 в 17:23
  • SVick быстро становится ответом Paul Erdős TDF. Я уверен, что через несколько лет мы будем использовать номер SVick для измерения расстояния между нашими сверстниками и его ...; ) –  VoteCoffee 14.08.2014 в 03:51
  • @svick Этот ответ правильный, если целевые блоки имеют неограниченную емкость. Если цели ограничены, и вы ожидаете, что исходный блок будет дросселировать себя, а не просто отбрасывать сообщения, нулевая цель должна иметь предикат, который является отрицанием предыдущих целевых предикатов. –  Johnbot 16.03.2018 в 13:37
Показать остальные комментарии
7

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

var forwarder = new BufferBlock<SomeType>();
forwarder.LinkTo(target1, item => matchesTarget1(item));
forwarder.LinkTo(target2, item => matchesTarget2(item));
forwarder.LinkTo(DataflowBlock.NullTarget<SomeType>(), item => !matchesTarget1(item) && !matchesTarget2(item));
    
ответ дан mycelo 19.06.2015 в 21:55
  • Не могли бы вы рассказать об уменьшении сообщений? Фильтр применяется последовательно, поэтому, если ссылка выполняется в правильном порядке, невозможно отбросить действительное сообщение, верно? –  VMAtm 04.11.2016 в 17:56
  • @VMAtm Итак, наконец-то, упорядочивает порядок по последовательности? Просто хочу убедиться ... –  b.ben 28.03.2017 в 18:34
  • @ b.ben Да, это работает так –  VMAtm 28.03.2017 в 18:54
  • @VMAtm, если ваш целевой блок имеет ограниченную емкость, а входная очередь заполнена, блок-источник не может определить, было ли это отклонено из-за того, что предикат или очередь заполнены. Это приведет к «отброшенным» сообщениям, если ваш конечный пункт назначения является пустой целью без предиката. Я потерял много времени для этого, потому что, казалось бы, каждая запись в Dataflow получает эту деталь неправильно. –  Johnbot 16.03.2018 в 13:15