Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/326.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# TransformBlock项卡在输出队列中。为什么以及如何修复?_C#_.net_Async Await_Task Parallel Library_Tpl Dataflow - Fatal编程技术网

C# TransformBlock项卡在输出队列中。为什么以及如何修复?

C# TransformBlock项卡在输出队列中。为什么以及如何修复?,c#,.net,async-await,task-parallel-library,tpl-dataflow,C#,.net,Async Await,Task Parallel Library,Tpl Dataflow,我已经浏览了TPL数据流,并使用链接到ActionBlock的trasforblock在代码中遇到了一个非常恼人的问题 最后,我发现items被困在TransformBlock的输出队列中,因为它的OutputCount属性连续返回大于“0”的值。 这就是整个应用程序死锁的原因。但是,只要我调用TransformBlock.TryReceiveAll(),它就会解除阻塞 任何人,请告诉我,如果有什么我错过了或如何防止这种行为 static void Main() { int total

我已经浏览了TPL数据流,并使用链接到
ActionBlock
trasforblock
在代码中遇到了一个非常恼人的问题

最后,我发现items被困在
TransformBlock
的输出队列中,因为它的
OutputCount
属性连续返回大于“0”的值。 这就是整个应用程序死锁的原因。但是,只要我调用
TransformBlock.TryReceiveAll()
,它就会解除阻塞

任何人,请告诉我,如果有什么我错过了或如何防止这种行为

static void Main()
{
    int total = 0;
    int itemsProcessing = 0;

    TransformBlock<int, Tuple<int, double>> transformBlock = new TransformBlock<int, Tuple<int, double>>(
        i => new Tuple<int, double>(i, Math.Sqrt(i)),
        new ExecutionDataflowBlockOptions
        {
            BoundedCapacity = 20,
            MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded
        });

    ActionBlock<Tuple<int, double>> outputBlock = new ActionBlock<Tuple<int, double>>(async tuple =>
        {
            await Task.Delay(1000); // simulating data output delay
            Interlocked.Decrement(ref itemsProcessing);
        },
        new ExecutionDataflowBlockOptions
        {
            BoundedCapacity = 5,
            MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded
        });

    transformBlock.Completion.ContinueWith(t => outputBlock.Complete());

    using (Timer timer = new Timer(o =>
        {
            Console.Title = string.Format(
                "{0}: {1}/{2} {3}/{4}/{5}",
                Assembly.GetExecutingAssembly().GetName().Name,
                Volatile.Read(ref itemsProcessing), Volatile.Read(ref total),
                transformBlock.InputCount, transformBlock.OutputCount, outputBlock.InputCount);
        }, null, 100, 100))
    {
        using (transformBlock.LinkTo(outputBlock, new DataflowLinkOptions { PropagateCompletion = true }))
        {
            for (int i = 0; i < 40; i++)
            {
                Thread.Sleep(100); // simulating new item retrieval delay

                Interlocked.Increment(ref total);
                Interlocked.Increment(ref itemsProcessing);

                transformBlock.SendAsync(i).Wait();
            }
        }

        Console.WriteLine("Enqueued");

        transformBlock.Complete();
        outputBlock.Completion.Wait();

        Console.WriteLine("Finish");

        timer.Change(Timeout.Infinite, Timeout.Infinite);
        timer.Dispose();
    }
}
static void Main()
{
int-total=0;
int itemsProcessing=0;
TransformBlock TransformBlock=新TransformBlock(
i=>新元组(i,Math.Sqrt(i)),
新的ExecutionDataflowBlockOptions
{
边界容量=20,
MaxDegreeOfParallelism=DataflowBlockOptions.Unbounded
});
ActionBlock outputBlock=新的ActionBlock(异步元组=>
{
等待任务。延迟(1000);//模拟数据输出延迟
联锁。减量(参考项目处理);
},
新的ExecutionDataflowBlockOptions
{
边界容量=5,
MaxDegreeOfParallelism=DataflowBlockOptions.Unbounded
});
transformBlock.Completion.ContinueWith(t=>outputBlock.Complete());
使用(计时器=新计时器)(o=>
{
Console.Title=string.Format(
"{0}: {1}/{2} {3}/{4}/{5}",
Assembly.GetExecutionGassembly().GetName().Name,
Volatile.Read(参考项处理),Volatile.Read(参考总数),
transformBlock.InputCount、transformBlock.OutputCount、outputBlock.InputCount);
},空,100100)
{
使用(transformBlock.LinkTo(outputBlock,新数据流链接选项{PropagateCompletion=true}))
{
对于(int i=0;i<40;i++)
{
Thread.Sleep(100);//模拟新项目检索延迟
联锁增量(参考总数);
联锁增量(参考项目处理);
transformBlock.SendAsync(i.Wait();
}
}
控制台写入线(“排队”);
transformBlock.Complete();
outputBlock.Completion.Wait();
控制台。写入线(“完成”);
timer.Change(Timeout.Infinite,Timeout.Infinite);
timer.Dispose();
}
}

调用
TransformBlock.LinkTo
将返回一次性注册。处理该注册后,块将取消链接

您的
使用
范围结束得太快,在
转换块
之前,这些块取消链接,有机会将自身清空到
操作块
中,从而使其无法完成。由于第一个区块没有完成,下一个区块甚至没有开始完成,更不用说完成了

使用块移动
中的等待可以解决死锁:

using (transformBlock.LinkTo(outputBlock, new DataflowLinkOptions { PropagateCompletion = true }))
{
    for (int i = 0; i < 40; i++)
    {
        Thread.Sleep(100); // simulating new item retrieval delay

        Interlocked.Increment(ref total);
        Interlocked.Increment(ref itemsProcessing);

        transformBlock.SendAsync(i).Wait();
    }

    Console.WriteLine("Enqueued");
    transformBlock.Complete();
    outputBlock.Completion.Wait();
    Console.WriteLine("Finish");
}
使用(transformBlock.LinkTo(outputBlock,新的DataflowLinkOptions{PropagateCompletion=true}))
{
对于(int i=0;i<40;i++)
{
Thread.Sleep(100);//模拟新项目检索延迟
联锁增量(参考总数);
联锁增量(参考项目处理);
transformBlock.SendAsync(i.Wait();
}
控制台写入线(“排队”);
transformBlock.Complete();
outputBlock.Completion.Wait();
控制台。写入线(“完成”);
}
作为补充说明,您真的不应该以这种方式阻塞异步代码。使用async Wait代替
Wait()
Task.Delay
代替
Thread.Sleep
等会简单得多


另外,由于您使用的是
PropagateCompletion
,因此不需要显式调用
outputBlock.Complete()

i3arnon,谢谢,非常有用。至于旁注,这只是一个测试应用程序,当然在现实世界中,我会尝试使用async/await,而不是简单的阻塞。再次感谢。