TPL Dataflow Broadcastblock丢弃最后一条消息

时间:2017-07-31 06:27:17

标签: c# .net unit-testing tpl-dataflow

我有一个非常简单的问题。我需要一种方法来轻松地对需要一些时间的消息执行一些处理。处理时,可能会输入新请求,但可以丢弃除最后一个请求之外的所有请求。

所以我认为TPL union FLOAT { float value; unsigned int bits; unsigned char bytes[4]; }; struct FLOAT { unsigned int sign; unsigned int exponent; unsigned int significand; }; union FLOAT num; struct FLOAT num_parts; 应该这样做,查看文档和帖子,例如StackExchange。我创建了以下解决方案并为其添加了一些单元测试,但在单元测试中,有时不会发送最后一项。

这不是我的预期。如果它应该丢弃任何东西,我会说它应该删除第一个项目,因为如果它不能处理消息,它应该覆盖它的1缓冲区。任何人都可以看到它是什么吗? 任何帮助将不胜感激!

这是块的代码:

Broadcastblock

以下是测试代码:

/// <summary>
/// This block will take items and perform the specified action on it. Any incoming messages while the action is being performed
/// will be discarded.
/// </summary>
public class DiscardWhileBusyActionBlock<T> : ITargetBlock<T>
{
    private readonly BroadcastBlock<T> broadcastBlock;

    private readonly ActionBlock<T> actionBlock;

    /// <summary>
    /// Initializes a new instance of the <see cref="DiscardWhileBusyActionBlock{T}"/> class.
    /// Constructs a SyncFilterTarget{TInput}.
    /// </summary>
    /// <param name="actionToPerform">Thing to do.</param>
    public DiscardWhileBusyActionBlock(Action<T> actionToPerform)
    {
        if (actionToPerform == null)
        {
            throw new ArgumentNullException(nameof(actionToPerform));
        }

        this.broadcastBlock = new BroadcastBlock<T>(item => item);
        this.actionBlock = new ActionBlock<T>(actionToPerform, new ExecutionDataflowBlockOptions { BoundedCapacity = 1, MaxDegreeOfParallelism = 1 });
        this.broadcastBlock.LinkTo(this.actionBlock);
        this.broadcastBlock.Completion.ContinueWith(task => this.actionBlock.Complete());
    }

    public DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> source, bool consumeToAccept)
    {
        return ((ITargetBlock<T>)this.broadcastBlock).OfferMessage(messageHeader, messageValue, source, consumeToAccept);
    }

    public void Complete()
    {
        this.broadcastBlock.Complete();
    }

    public void Fault(Exception exception)
    {
        ((ITargetBlock<T>)this.broadcastBlock).Fault(exception);
    }

    public Task Completion => this.actionBlock.Completion;
}

1 个答案:

答案 0 :(得分:0)

当您将操作块的BoundedCapacity放到1时,这意味着,如果它正在处理,并且已经在其队列中有项目,它将丢弃该消息,这将超出范围。所以基本上发生的事情就是你的块做了它的工作,在缓冲区满了时拒绝新的消息。之后广播块完成,因为整个消息被发送给收件人,并调用Completion,完成整个管道。

您需要为最后的消息检查返回的布尔值Post,或者更可能的是,将最后一条消息存储在某个变量中,确保它将转到管道。看起来您最好不要使用BroadcastBlock,因为它的目的是为链接块的数量提供消息的副本,并且只需自己编写逻辑。也许您可以使用简单的BufferBlock代替。

更新OfferMessage方法也会提供有关所提供消息的信息。我认为您根本不需要缓冲区块,因为您必须处理管道的非默认逻辑。更容易拥有_lastMessage之类的字段,将最后一个值存储在其中,并在actionBlock接受请求时删除它。您甚至可以完全删除数据流依赖关系,因为您所做的只是为请求调用方法。

附注:您可以link blocks with completion propagation设置选项:

var linkOptions = new DataflowLinkOptions { PropagateCompletion = true };
this.broadcastBlock.LinkTo(this.actionBlock, linkOptions);

这可以删除部分使用potentially dangerous ContinueWith代码的代码。如果您需要异步行为,也可以await broadcastBlock.SendAsync()而不是Post