Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/259.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# TPL数据流,我可以查询数据块是否标记为已完成但尚未完成吗?_C#_Concurrency_Task Parallel Library_Message Passing_Tpl Dataflow - Fatal编程技术网

C# TPL数据流,我可以查询数据块是否标记为已完成但尚未完成吗?

C# TPL数据流,我可以查询数据块是否标记为已完成但尚未完成吗?,c#,concurrency,task-parallel-library,message-passing,tpl-dataflow,C#,Concurrency,Task Parallel Library,Message Passing,Tpl Dataflow,鉴于以下情况: BufferBlock<int> sourceBlock = new BufferBlock<int>(); TransformBlock<int, int> targetBlock = new TransformBlock<int, int>(element => { return element * 2; }); sourceBlock.LinkTo(targetBlock, new DataflowLinkOp

鉴于以下情况:

BufferBlock<int> sourceBlock = new BufferBlock<int>();
TransformBlock<int, int> targetBlock = new TransformBlock<int, int>(element =>
{
    return element * 2;
});

sourceBlock.LinkTo(targetBlock, new DataflowLinkOptions { PropagateCompletion = true });

//feed some elements into the buffer block
for(int i = 1; i <= 1000000; i++)
{
    sourceBlock.SendAsync(i);
}

sourceBlock.Complete();

targetBlock.Completion.ContinueWith(_ =>
{
    //notify completion of the target block
});
BufferBlock sourceBlock=new BufferBlock();
TransformBlock targetBlock=新TransformBlock(元素=>
{
返回元素*2;
});
LinkTo(targetBlock,新数据流链接选项{PropagateCompletion=true});
//将一些元素输入缓冲块
对于(int i=1;i
{
//通知目标区块的完成
});
targetBlock
似乎从未完成,我认为原因是
TransformBlock
targetBlock
中的所有项目都在输出队列中等待,因为我没有将
targetBlock
链接到任何其他数据流块。然而,我实际想要实现的是当(a)通知
targetBlock
完成时的通知,以及(B)输入队列为空时的通知。我不想关心项目是否仍然位于
TransformBlock
的输出队列中。我该怎么做呢?获取我想要查询的
sourceBlock
的完成状态并确保
targetBlock
InputCount
为零的唯一方法是什么?我不确定这是否非常稳定(只有当
sourceBlock
中的最后一项已传递给
targetBlock
时,
sourceBlock
才真正标记为已完成)。有没有一种更优雅、更有效的方法来达到同样的目标


编辑:我刚刚注意到,即使是检查
sourceBlock
targetBlock
InputCount
是否为零的“脏”方法也不容易实现。那个街区在哪里?它不能在
targetBlock
内,因为一旦满足上述两个条件,显然
targetBlock
内不再处理任何消息。同时,检查
源代码块的完成状态也会带来很多低效。

我相信您不能直接这样做。您可以使用反射从一些
private
字段获取此信息,但我不建议这样做

但可以通过创建自定义块来实现这一点。在
Complete()
的情况下,很简单:只需创建一个块,将每个方法转发到原始块。除了
Complete()
,它还将记录它

在计算所有项目的处理何时完成的情况下,您可以将块链接到中间
缓冲块
。这样,输出队列将快速清空,因此检查内部块的
Completed
,可以相当准确地测量处理何时完成。这会影响您的测量,但希望不会太大


另一个选项是在块的委托末尾添加一些日志记录。这样,您可以看到最后一项的处理何时完成。

首先,使用IPropagator块作为叶终端是不正确的。但是仍然可以通过异步检查TargetBlock的输出缓冲区中是否有输出消息,然后使用这些消息,从而清空缓冲区,从而满足您的需求

    `  BufferBlock<int> sourceBlock = new BufferBlock<int>();
       TransformBlock<int, int> targetBlock = new TransformBlock<int, int> 
       (element =>
       {

        return element * 2;
        });
        sourceBlock.LinkTo(targetBlock, new DataflowLinkOptions { 
        PropagateCompletion = true });

        //feed some elements into the buffer block
        for (int i = 1; i <= 100; i++)
        {
             sourceBlock.SendAsync(i);
        }

        sourceBlock.Complete();

        bool isOutputAvailable = await targetBlock.OutputAvailableAsync();
        while(isOutputAvailable)
        {
            int value = await targetBlock.ReceiveAsync();

            isOutputAvailable = await targetBlock.OutputAvailableAsync();
        }


        await targetBlock.Completion.ContinueWith(_ =>
        {
            Console.WriteLine("Target Block Completed");//notify completion of the target block
        });
`BufferBlock sourceBlock=new BufferBlock();
TransformBlock targetBlock=新TransformBlock
(元素=>
{
返回元素*2;
});
LinkTo(targetBlock,新数据流链接选项{
完成=真});
//将一些元素输入缓冲块
对于(int i=1;i
{
Console.WriteLine(“目标块已完成”);//通知目标块的完成
});

`

如果
TransformBlock
有一个
ProcessingCompleted
事件,该事件将在块已完成其队列中所有消息的处理时触发,但不存在此类事件,那就太好了。以下是纠正这一遗漏的尝试。
CreateTransformBlockEx
方法接受一个
操作
处理程序,该处理程序在发生此“事件”时被调用

其目的是始终在块最终完成之前调用处理程序。不幸的是,在所提供的被取消的情况下,完成(取消)首先发生,并且处理程序在几毫秒后被调用。要解决这种不一致性,需要一些棘手的解决办法,并且可能会产生其他不必要的副作用,所以我将保持原样

public static IPropagatorBlock<TInput, TOutput>
    CreateTransformBlockEx<TInput, TOutput>(Func<TInput, Task<TOutput>> transform,
    Action<Exception> onProcessingCompleted,
    ExecutionDataflowBlockOptions dataflowBlockOptions = null)
{
    if (onProcessingCompleted == null)
        throw new ArgumentNullException(nameof(onProcessingCompleted));
    dataflowBlockOptions = dataflowBlockOptions ?? new ExecutionDataflowBlockOptions();

    var transformBlock = new TransformBlock<TInput, TOutput>(transform,
        dataflowBlockOptions);
    var bufferBlock = new BufferBlock<TOutput>(dataflowBlockOptions);

    transformBlock.LinkTo(bufferBlock);
    PropagateCompletion(transformBlock, bufferBlock, onProcessingCompleted);
    return DataflowBlock.Encapsulate(transformBlock, bufferBlock);

    async void PropagateCompletion(IDataflowBlock block1, IDataflowBlock block2,
        Action<Exception> completionHandler)
    {
        try
        {
            await block1.Completion.ConfigureAwait(false);
        }
        catch { }
        var exception = 
            block1.Completion.IsFaulted ? block1.Completion.Exception : null;
        try
        {
            // Invoke the handler before completing the second block
            completionHandler(exception);
        }
        finally
        {
            if (exception != null) block2.Fault(exception); else block2.Complete();
        }
    }
}

// Overload with synchronous lambda
public static IPropagatorBlock<TInput, TOutput>
    CreateTransformBlockEx<TInput, TOutput>(Func<TInput, TOutput> transform,
    Action<Exception> onProcessingCompleted,
    ExecutionDataflowBlockOptions dataflowBlockOptions = null)
{
    return CreateTransformBlockEx<TInput, TOutput>(
        x => Task.FromResult(transform(x)), onProcessingCompleted,
        dataflowBlockOptions);
}

为什么您需要知道这一点?因为一旦targetBlock中的所有项目完成处理,即使项目仍在outQueue中,我也会完成一个流程。一个原因是该过程的完成,另一个原因是对延迟、吞吐量和性能的测量。链接的下一个数据块可能需要更长的时间来处理OutQueue中的所有剩余项为什么不等待源块的任务?您不需要将块附加到该块,只需要源块的
Completion
属性上的一个延续。如果您想清空
TransformBlock
的缓冲区,您可以将其链接到如下所示的:
targetBlock.LinkTo(DataflowBlock.NullTarget())
var httpClient = new HttpClient();
var downloader = CreateTransformBlockEx<string, string>(async url =>
{
    return await httpClient.GetStringAsync(url);
}, onProcessingCompleted: ex =>
{
    Console.WriteLine($"Download completed {(ex == null ? "OK" : "Error")}");
}, new ExecutionDataflowBlockOptions()
{
    MaxDegreeOfParallelism = 10
});