簡體   English   中英

TPL數據流反饋循環

[英]TPL dataflow feedback loop

管道獲取文件的絕對路徑(在Block 1 )並對其進行處理,然后將其保存到數據庫中(在block3 )。

約束是某些文件類型( .vde)取決於特定的父文件類型( .vd)。 父文件類型具有處理從屬文件類型所需的元數據。 除非系統中存在父文件類型,否則無法處理從屬文件類型。

目標-我需要系統以某種方式等待父文件類型進入系統並更新狀態。 然后,將自動再次調用並處理從屬文件類型。

我的方法-添加一個鏈接到block 1的反饋回路( block 4 )。 但是, 我最終丟失了消息。 block 4block 1的消息無法到達block 3並且在管道中的某個位置之間丟失。

我怎樣做才能不丟失從第block 4 block 1到第block 1 block 4的消息?

還是可以用更好的方式做到這一點?

數據處理管道

作為練習,我嘗試使JoinDependencyBlockJoinBlock<T1,T2>類似,該JoinBlock<T1,T2>從兩個緩沖區傳播匹配的元素。


更新 :我想出了一個更簡單的實現,該實現內部使用三個內置塊,兩個ActionBlock用於輸入,一個BufferBlock用於輸出。 每個操作塊都會填充一個專用的List ,並且在添加元素時會在兩個列表中搜索匹配對。 如果找到一個,則將其發布到BufferBlock 主要的復雜性在於這些模塊的鏈接,因為成功和失敗案例需要不同的處理方式。

JoinDependencyBlock完成后,內部列表中所有不匹配的元素都將被丟棄。

public class JoinDependencyBlock<T1, T2> : ISourceBlock<(T1, T2)>
{
    private readonly Func<T1, T2, bool> _matchPredicate;
    private readonly List<T1> _list1 = new List<T1>();
    private readonly List<T2> _list2 = new List<T2>();
    private readonly ActionBlock<T1> _input1;
    private readonly ActionBlock<T2> _input2;
    private readonly BufferBlock<(T1, T2)> _output;
    private readonly object _locker = new object();

    public JoinDependencyBlock(Func<T1, T2, bool> matchPredicate,
        CancellationToken cancellationToken)
    {
        _matchPredicate = matchPredicate
            ?? throw new ArgumentNullException(nameof(matchPredicate));

        // Create the three internal blocks
        var options = new ExecutionDataflowBlockOptions()
        {
            CancellationToken = cancellationToken
        };
        _input1 = new ActionBlock<T1>(Add1, options);
        _input2 = new ActionBlock<T2>(Add2, options);
        _output = new BufferBlock<(T1, T2)>(options);

        // Link the input blocks with the output block
        var inputTasks = new Task[] { _input1.Completion, _input2.Completion };
        Task.WhenAny(inputTasks).Unwrap().ContinueWith(t =>
        {
            // If ANY input block fails, then the whole block has failed
            ((IDataflowBlock)_output).Fault(t.Exception.InnerException);
            if (!_input1.Completion.IsCompleted) _input1.Complete();
            if (!_input2.Completion.IsCompleted) _input2.Complete();
            ClearLists();
        }, default, TaskContinuationOptions.OnlyOnFaulted |
            TaskContinuationOptions.RunContinuationsAsynchronously,
            TaskScheduler.Default);
        Task.WhenAll(inputTasks).ContinueWith(t =>
        {
            // If ALL input blocks succeeded, then the whole block has succeeded
            _output.Complete();
            ClearLists();
        }, default, TaskContinuationOptions.NotOnFaulted |
            TaskContinuationOptions.RunContinuationsAsynchronously,
            TaskScheduler.Default);
    }

    public JoinDependencyBlock(Func<T1, T2, bool> matchPredicate)
        : this(matchPredicate, CancellationToken.None) { }

    public ITargetBlock<T1> Target1 => _input1;
    public ITargetBlock<T2> Target2 => _input2;
    public Task Completion => _output.Completion;

    private void Add1(T1 value1)
    {
        T2 value2;
        lock (_locker)
        {
            var index = _list2.FindIndex(v => _matchPredicate(value1, v));
            if (index < 0)
            {
                // Match not found
                _list1.Add(value1);
                return;
            }
            value2 = _list2[index];
            _list2.RemoveAt(index);
        }
        _output.Post((value1, value2));
    }

    private void Add2(T2 value2)
    {
        T1 value1;
        lock (_locker)
        {
            var index = _list1.FindIndex(v => _matchPredicate(v, value2));
            if (index < 0)
            {
                // Match not found
                _list2.Add(value2);
                return;
            }
            value1 = _list1[index];
            _list1.RemoveAt(index);
        }
        _output.Post((value1, value2));
    }

    private void ClearLists()
    {
        lock (_locker)
        {
            _list1.Clear();
            _list2.Clear();
        }
    }

    public void Complete() => _output.Complete();

    public void Fault(Exception exception)
        => ((IDataflowBlock)_output).Fault(exception);

    public IDisposable LinkTo(ITargetBlock<(T1, T2)> target,
        DataflowLinkOptions linkOptions)
        => _output.LinkTo(target, linkOptions);

    (T1, T2) ISourceBlock<(T1, T2)>.ConsumeMessage(
        DataflowMessageHeader messageHeader, ITargetBlock<(T1, T2)> target,
        out bool messageConsumed)
        => ((ISourceBlock<(T1, T2)>)_output).ConsumeMessage(
            messageHeader, target, out messageConsumed);

    void ISourceBlock<(T1, T2)>.ReleaseReservation(
        DataflowMessageHeader messageHeader, ITargetBlock<(T1, T2)> target)
        => ((ISourceBlock<(T1, T2)>)_output).ReleaseReservation(
            messageHeader, target);

    bool ISourceBlock<(T1, T2)>.ReserveMessage(
        DataflowMessageHeader messageHeader, ITargetBlock<(T1, T2)> target)
        => ((ISourceBlock<(T1, T2)>)_output).ReserveMessage(
            messageHeader, target);
}

用法示例:

var joinBlock = new JoinDependencyBlock<FileInfo, FileInfo>((fi1, fi2) =>
{
    // Check if the files are matched
    var name1 = Path.GetFileNameWithoutExtension(fi1.Name);
    var name2 = Path.GetFileNameWithoutExtension(fi2.Name);
    return StringComparer.OrdinalIgnoreCase.Equals(name1, name2);
});
var actionBlock = new ActionBlock<(FileInfo, FileInfo)>(pair =>
{
    // Process the matching files
    Console.WriteLine(pair.Item1.Name + " :: " +  pair.Item2.Name);
});
joinBlock.LinkTo(actionBlock);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM