public JoinBlock(GroupingDataflowBlockOptions dataflowBlockOptions)
        {
            if (dataflowBlockOptions == null)
            {
                throw new ArgumentNullException("dataflowBlockOptions");
            }

            this.dataflowBlockOptions = dataflowBlockOptions;
            this.compHelper           = new CompletionHelper(dataflowBlockOptions);

            target1 = new JoinTarget <T1> (this, SignalArrivalTarget, compHelper,
                                           () => outgoing.IsCompleted, dataflowBlockOptions,
                                           dataflowBlockOptions.Greedy, TryAdd1);
            target2 = new JoinTarget <T2> (this, SignalArrivalTarget, compHelper,
                                           () => outgoing.IsCompleted, dataflowBlockOptions,
                                           dataflowBlockOptions.Greedy, TryAdd2);
            target3 = new JoinTarget <T3> (this, SignalArrivalTarget, compHelper,
                                           () => outgoing.IsCompleted, dataflowBlockOptions,
                                           dataflowBlockOptions.Greedy, TryAdd3);
            outgoing = new OutgoingQueue <Tuple <T1, T2, T3> > (
                this, compHelper,
                () => target1.Buffer.IsCompleted || target2.Buffer.IsCompleted ||
                target3.Buffer.IsCompleted,
                _ =>
            {
                target1.DecreaseCount();
                target2.DecreaseCount();
                target3.DecreaseCount();
            }, dataflowBlockOptions);
        }
        public BatchedJoinBlock(int batchSize,
                                GroupingDataflowBlockOptions dataflowBlockOptions)
        {
            if (batchSize <= 0)
            {
                throw new ArgumentOutOfRangeException(
                          "batchSize", batchSize, "The batchSize must be positive.");
            }
            if (dataflowBlockOptions == null)
            {
                throw new ArgumentNullException("dataflowBlockOptions");
            }
            if (!dataflowBlockOptions.Greedy)
            {
                throw new ArgumentException(
                          "Greedy must be true for this dataflow block.", "dataflowBlockOptions");
            }
            if (dataflowBlockOptions.BoundedCapacity != DataflowBlockOptions.Unbounded)
            {
                throw new ArgumentException(
                          "BoundedCapacity must be Unbounded or -1 for this dataflow block.",
                          "dataflowBlockOptions");
            }

            BatchSize        = batchSize;
            options          = dataflowBlockOptions;
            completionHelper = CompletionHelper.GetNew(options);

            target1 = new JoinTarget <T1> (
                this, SignalTarget, completionHelper, () => outgoing.IsCompleted,
                dataflowBlockOptions, true, TryAdd);
            target2 = new JoinTarget <T2> (
                this, SignalTarget, completionHelper, () => outgoing.IsCompleted,
                dataflowBlockOptions, true, TryAdd);
            target3 = new JoinTarget <T3> (
                this, SignalTarget, completionHelper, () => outgoing.IsCompleted,
                dataflowBlockOptions, true, TryAdd);

            outgoing = new OutgoingQueue <Tuple <IList <T1>, IList <T2>, IList <T3> > > (
                this, completionHelper,
                () => target1.Buffer.IsCompleted || target2.Buffer.IsCompleted ||
                target3.Buffer.IsCompleted,
                _ =>
            {
                target1.DecreaseCount();
                target2.DecreaseCount();
                target3.DecreaseCount();
            }, options);
        }