C# 阻止BroadcastBlock在LinkTo上发送缓冲消息

C# 阻止BroadcastBlock在LinkTo上发送缓冲消息,c#,task-parallel-library,tpl-dataflow,C#,Task Parallel Library,Tpl Dataflow,给定缓冲区中有消息的BroadcastBlock,是否可以阻止该消息发送到新链接的目标?例如: static void Main(字符串[]args) { var myBroadcastBlock=newbroadcastblock(msg=>msg); var myActionBlock=newactionblock(msg=>Console.WriteLine(msg)); myBroadcastBlock.Post(“Hello World!”);//此处没有链接的目标。 myBroadc

给定缓冲区中有消息的
BroadcastBlock
,是否可以阻止该消息发送到新链接的目标?例如:

static void Main(字符串[]args)
{
var myBroadcastBlock=newbroadcastblock(msg=>msg);
var myActionBlock=newactionblock(msg=>Console.WriteLine(msg));
myBroadcastBlock.Post(“Hello World!”);//此处没有链接的目标。
myBroadcastBlock.LinkTo(myActionBlock);//链接目标。
//等等。
}
此代码将打印“Hello World”。基本上,
BroadcastBlock
仍会将缓冲消息发送到
.LinkTo
上的
ActionBlock
,尽管消息在建立链接之前已发布

是否有一种内置的方法来防止这种行为?我只希望消息被发送到当前的链接,而不是未来的


我正在使用

此行为在使用内置类时是不可能的。其行为是不可配置的。如果您迫切需要这种行为,可以尝试下面的实现。它使用一个内部的
广播块
,该块的索引随着每一条新消息的增加而增加,以便在链接期间可以过滤掉当前活动的消息

由于需要从
T
转换到
(T,long)
并返回到
T
,所以
BroadcastBlockNewOnly
类中有很多间接操作。这使得类很难维护,而且效率也不是很高。在每个接收到的消息上都会分配一个新对象,从而为垃圾收集器创建更多的工作,因此请谨慎使用此类

public class BroadcastBlockNewOnly<T> : ITargetBlock<T>, ISourceBlock<T>
{
    private readonly IPropagatorBlock<(T, long), (T, long)> _broadcastBlock;
    private long _index;

    public BroadcastBlockNewOnly(Func<T, T> cloningFunction,
        DataflowBlockOptions dataflowBlockOptions = null)
    {
        if (cloningFunction == null)
            throw new ArgumentNullException(nameof(cloningFunction));
        _broadcastBlock = new BroadcastBlock<(T, long)>(entry =>
        {
            var (value, index) = entry;
            return (cloningFunction(value), index);
        }, dataflowBlockOptions ?? new DataflowBlockOptions());
    }

    public Task Completion => _broadcastBlock.Completion;
    public void Complete() => _broadcastBlock.Complete();
    void IDataflowBlock.Fault(Exception ex) => _broadcastBlock.Fault(ex);

    public IDisposable LinkTo(ITargetBlock<T> target, DataflowLinkOptions linkOptions)
    {
        if (target == null) throw new ArgumentNullException(nameof(target));
        var currentIndex = Interlocked.CompareExchange(ref _index, 0, 0);
        var linkedTargetProxy = new LinkedTargetProxy(target, this, currentIndex);
        return _broadcastBlock.LinkTo(linkedTargetProxy, linkOptions);
    }

    private long GetNewIndex() => Interlocked.Increment(ref _index);

    DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader header,
        T value, ISourceBlock<T> source, bool consumeToAccept)
    {
        var sourceProxy = source != null ?
            new SourceProxy(source, this, GetNewIndex) : null;
        return _broadcastBlock.OfferMessage(header, (value, GetNewIndex()),
            sourceProxy, consumeToAccept);
    }

    T ISourceBlock<T>.ConsumeMessage(DataflowMessageHeader header,
        ITargetBlock<T> target, out bool messageConsumed)
    {
        var targetProxy = target != null ? new TargetProxy(target, this) : null;
        var (value, index) = _broadcastBlock.ConsumeMessage(header, targetProxy,
            out messageConsumed);
        return value;
    }

    bool ISourceBlock<T>.ReserveMessage(DataflowMessageHeader header,
        ITargetBlock<T> target)
    {
        var targetProxy = target != null ? new TargetProxy(target, this) : null;
        return _broadcastBlock.ReserveMessage(header, targetProxy);
    }

    void ISourceBlock<T>.ReleaseReservation(DataflowMessageHeader header,
        ITargetBlock<T> target)
    {
        var targetProxy = target != null ? new TargetProxy(target, this) : null;
        _broadcastBlock.ReleaseReservation(header, targetProxy);
    }

    private class LinkedTargetProxy : ITargetBlock<(T, long)>
    {
        private readonly ITargetBlock<T> _realTarget;
        private readonly ISourceBlock<T> _realSource;
        private readonly long _indexLimit;

        public LinkedTargetProxy(ITargetBlock<T> realTarget, ISourceBlock<T> realSource,
            long indexLimit)
        {
            _realTarget = realTarget;
            _realSource = realSource;
            _indexLimit = indexLimit;
        }

        DataflowMessageStatus ITargetBlock<(T, long)>.OfferMessage(
            DataflowMessageHeader header, (T, long) messageValue,
            ISourceBlock<(T, long)> source, bool consumeToAccept)
        {
            var (value, index) = messageValue;
            if (index <= _indexLimit) return DataflowMessageStatus.Declined;
            return _realTarget.OfferMessage(header, value, _realSource, consumeToAccept);
        }

        Task IDataflowBlock.Completion => throw new NotSupportedException();
        void IDataflowBlock.Complete() => _realTarget.Complete();
        void IDataflowBlock.Fault(Exception ex) => _realTarget.Fault(ex);
    }

    private class SourceProxy : ISourceBlock<(T, long)>
    {
        private readonly ISourceBlock<T> _realSource;
        private readonly ITargetBlock<T> _realTarget;
        private readonly Func<long> _getNewIndex;

        public SourceProxy(ISourceBlock<T> realSource, ITargetBlock<T> realTarget,
            Func<long> getNewIndex)
        {
            _realSource = realSource;
            _realTarget = realTarget;
            _getNewIndex = getNewIndex;
        }

        (T, long) ISourceBlock<(T, long)>.ConsumeMessage(DataflowMessageHeader header,
            ITargetBlock<(T, long)> target, out bool messageConsumed)
        {
            var value = _realSource.ConsumeMessage(header, _realTarget,
                out messageConsumed);
            var newIndex = _getNewIndex();
            return (value, newIndex);
        }

        bool ISourceBlock<(T, long)>.ReserveMessage(DataflowMessageHeader header,
            ITargetBlock<(T, long)> target)
        {
            return _realSource.ReserveMessage(header, _realTarget);
        }

        void ISourceBlock<(T, long)>.ReleaseReservation(DataflowMessageHeader header,
            ITargetBlock<(T, long)> target)
        {
            _realSource.ReleaseReservation(header, _realTarget);
        }

        Task IDataflowBlock.Completion => throw new NotSupportedException();
        void IDataflowBlock.Complete() => throw new NotSupportedException();
        void IDataflowBlock.Fault(Exception ex) => throw new NotSupportedException();
        IDisposable ISourceBlock<(T, long)>.LinkTo(ITargetBlock<(T, long)> target,
            DataflowLinkOptions linkOptions) => throw new NotSupportedException();
    }

    private class TargetProxy : ITargetBlock<(T, long)>
    {
        private readonly ITargetBlock<T> _realTarget;
        private readonly ISourceBlock<T> _realSource;

        public TargetProxy(ITargetBlock<T> realTarget, ISourceBlock<T> realSource)
        {
            _realTarget = realTarget;
            _realSource = realSource;
        }

        DataflowMessageStatus ITargetBlock<(T, long)>.OfferMessage(
            DataflowMessageHeader header, (T, long) messageValue,
            ISourceBlock<(T, long)> source, bool consumeToAccept)
        {
            var (value, index) = messageValue;
            return _realTarget.OfferMessage(header, value, _realSource, consumeToAccept);
        }

        Task IDataflowBlock.Completion => throw new NotSupportedException();
        void IDataflowBlock.Complete() => throw new NotSupportedException();
        void IDataflowBlock.Fault(Exception ex) => throw new NotSupportedException();
    }

}
公共类BroadcastBlockNewOnly:ITargetBlock、ISourceBlock
{
私有只读IPropagatorBlock\u广播块;
私人多头指数;
公共广播BlockNewOnly(功能关闭功能,
DataflowBlockOptions(DataflowBlockOptions=null)
{
if(cloningFunction==null)
抛出新ArgumentNullException(nameof(cloningFunction));
_broadcastBlock=新的broadcastBlock(条目=>
{
var(价值、指数)=分录;
返回(cloningFunction(value)、index);
},dataflowBlockOptions??新建dataflowBlockOptions());
}
公共任务完成=>\u broadcastBlock.Completion;
public void Complete()=>\u broadcastBlock.Complete();
无效IDataflowBlock.Fault(异常ex)=>\u broadcastBlock.Fault(ex);
公共IDisposable链接到(ITargetBlock目标、DataflowLinkOptions链接选项)
{
如果(target==null)抛出新的ArgumentNullException(nameof(target));
var currentIndex=联锁比较交换(参考指数,0,0);
var linkedTargetProxy=新linkedTargetProxy(目标,此,当前索引);
返回_broadcastBlock.LinkTo(linkedTargetProxy,linkOptions);
}
private long GetNewIndex()=>Interlocked.Increment(ref\u index);
DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader标头,
T值,ISourceBlock源,bool consumeToAccept)
{
var sourceProxy=source!=null?
new SourceProxy(source、this、GetNewIndex):null;
return _broadcastBlock.OfferMessage(header,(value,GetNewIndex()),
sourceProxy,consumeToAccept);
}
T ISourceBlock.ConsumeMessage(DataflowMessageHeader标头,
ITargetBlock目标,输出bool消息(已消耗)
{
var targetProxy=target!=null?新建targetProxy(target,this):null;
var(值,索引)=_broadcastBlock.consumermessage(头,targetProxy,
外包装(已消耗);
返回值;
}
bool ISourceBlock.ReserveMessage(DataflowMessageHeader,
iTarget(块目标)
{
var targetProxy=target!=null?新建targetProxy(target,this):null;
返回_broadcastBlock.ReserveMessage(头,targetProxy);
}
作废ISourceBlock.ReleaseReservation(DataflowMessageHeader),
iTarget(块目标)
{
var targetProxy=target!=null?新建targetProxy(target,this):null;
_broadcastBlock.ReleaseReservation(标头,targetProxy);
}
私有类LinkedTargetProxy:ITargetBlock
{
私有只读ITargetBlock\u realTarget;
私有只读ISourceBlock\u realSource;
私有只读长索引限制;
公共链接的TargetProxy(ITargetBlock realTarget、ISourceBlock realSource、,
长指数限制)
{
_realTarget=realTarget;
_realSource=realSource;
_indexLimit=indexLimit;
}
DataflowMessageStatus ITargetBlock.OfferMessage(
DataflowMessageHeader标头,(T,long)messageValue,
ISourceBlock源,bool consumeToAccept)
{
var(值、指数)=消息值;
if(索引抛出新的NotSupportedException();
void IDataflowBlock.Complete()=>\u realTarget.Complete();
无效IDataflowBlock.Fault(异常ex)=>\u realTarget.Fault(ex);
}
私有类SourceProxy:ISourceBlock
{
私有只读ISourceBlock\u realSource;
私有只读ITargetBlock\u realTarget;
私有只读Func_getNewIndex;
public SourceProxy(ISourceBlock realSource、ITargetBlock realTarget、,
Func getNewIndex)
{
_realSource=realSource;
_realTarget=realTarget;
_getNewIndex=getNewIndex;
}
(T,long)ISourceBlock.ConsumeMessage(DataflowMessageHeader,
ITargetBlock目标,输出bool消息(已消耗)
{
var值=_realSource.consumerMessage(头,_realTarget,
外包装(已消耗);
var newIndex=_getNewIndex();
返回值(值、新索引);
}
bool ISourceBlock.ReserveMessage(DataflowMessageHeader,
iTarget(块目标)