简体   繁体   English

TPL.Dataflow - 在ActionBlock中发生无法处理的异常时阻止挂起<T>

[英]TPL.Dataflow - preventing hangs when unhanded exception occurs in ActionBlock<T>

Just starting with System.Threading.Tasks.Dataflow and not sure I understand proper error handling technique for unhandled exceptions in the ActionBlock . 刚从System.Threading.Tasks.Dataflow开始,并且不确定我理解ActionBlock未处理异常的正确错误处理技术。

What I have right now leads to hang: - ActionBlock had unhandled exception and is no longer processing - producer is not able to complete because it's over BoundedCapacity 我现在拥有的东西导致挂起: - ActionBlock有未处理的异常并且不再处理 - 生产者无法完成因为它超过了BoundedCapacity

Here is the code that I have (it's simplified to show one consumer). 这是我的代码(它被简化为显示一个消费者)。

internal class Program
{
    private static int _processCounter = 0;

    internal class MyClass
    {
        public MyClass(int id)
        {
            this.Id = id;
        }

        internal int Id { get; set; }
    }

    private static void Main(string[] args)
    {
        BufferBlock<MyClass> queue = new BufferBlock<MyClass>(new DataflowBlockOptions {BoundedCapacity = 10,});

        ActionBlock<MyClass> consumer =
            new ActionBlock<MyClass>(record => Process(record),
                new ExecutionDataflowBlockOptions {BoundedCapacity = 1,});

        queue.LinkTo(consumer, new DataflowLinkOptions {PropagateCompletion = true,});

        Task producer = Produce(queue);

        Trace.TraceInformation("Starting to wait on producer and consumer...");

        Task.WhenAll(producer, consumer.Completion).Wait(); // <-- this will hang. consumer.Completion is faulted, but producer is still "running".

    }

    private static async Task Produce(BufferBlock<MyClass> queue)
    {
        for (int i = 0; i < 20; i++)
        {
            await queue.SendAsync(new MyClass(i));
            Trace.TraceInformation("Sending object number {0}", i);
            await Task.Delay(1);
        }
        Trace.TraceInformation("Completing the producer");
        queue.Complete();
            // <-- we never get here because one of the SendAsync will be waiting to not excede BoundedCapacity = 10
    }

    private static void Process(MyClass myClass)
    {
        int counter = Interlocked.Increment(ref _processCounter);
        Trace.TraceInformation("Processing object number {0}", myClass.Id);
        if (counter > 4)
        {
            Trace.TraceInformation("About to throw exception for object {0}", myClass.Id);
            throw new ArgumentException("Something bad happened");
        }
    }
}

Output: 输出:

ConsoleApplication5.vshost.exe Information: 0 : Sending object number 0
ConsoleApplication5.vshost.exe Information: 0 : Starting to wait on producer and consumer...
ConsoleApplication5.vshost.exe Information: 0 : Sending object number 1
ConsoleApplication5.vshost.exe Information: 0 : Processing object number 0
ConsoleApplication5.vshost.exe Information: 0 : Sending object number 2
ConsoleApplication5.vshost.exe Information: 0 : Sending object number 3
ConsoleApplication5.vshost.exe Information: 0 : Processing object number 1
ConsoleApplication5.vshost.exe Information: 0 : Sending object number 4
ConsoleApplication5.vshost.exe Information: 0 : Processing object number 2
ConsoleApplication5.vshost.exe Information: 0 : Processing object number 3
ConsoleApplication5.vshost.exe Information: 0 : Sending object number 5
ConsoleApplication5.vshost.exe Information: 0 : Processing object number 4
ConsoleApplication5.vshost.exe Information: 0 : About to throw exception for object 4
ConsoleApplication5.vshost.exe Information: 0 : Sending object number 6
A first chance exception of type 'System.ArgumentException' occurred in ConsoleApplication5.exe
ConsoleApplication5.vshost.exe Information: 0 : Sending object number 7
ConsoleApplication5.vshost.exe Information: 0 : Sending object number 8
ConsoleApplication5.vshost.exe Information: 0 : Sending object number 9
ConsoleApplication5.vshost.exe Information: 0 : Sending object number 10
ConsoleApplication5.vshost.exe Information: 0 : Sending object number 11
ConsoleApplication5.vshost.exe Information: 0 : Sending object number 12
ConsoleApplication5.vshost.exe Information: 0 : Sending object number 13
ConsoleApplication5.vshost.exe Information: 0 : Sending object number 14
<never finishes>

The question is, what is the proper way to wait for such execution to ensure it either completes or propagates exception. 问题是,等待执行这种执行以确保它完成或传播异常的正确方法是什么。 Thanks! 谢谢!

There's a lot you can do since it's about how you structure your code. 您可以做很多事情,因为它是关于您如何构建代码的。 The simplest is probably to use a CancellationToken for the producer and wait for the consumer first: 最简单的可能是为生产者使用CancellationToken并首先等待消费者:

private static void Main(string[] args)
{
    // ...

    var cts = new CancellationTokenSource();
    Task producer = Produce(queue, cts.Token);

    Trace.TraceInformation("Starting to wait on producer and consumer...");
    try
    {
        await consumer.Completion;
    }
    catch
    {
        cts.Cancel();
        // handle
    }

    try
    {
        await producer
    }
    catch
    {
        // handle
    }
}

private static async Task Produce(BufferBlock<MyClass> queue, CancellationToken token)
{
    for (int i = 0; i < 20; i++)
    {
        await queue.SendAsync(new MyClass(i), token);
        Trace.TraceInformation("Sending object number {0}", i);
        await Task.Delay(1);
    }
    Trace.TraceInformation("Completing the producer");
    queue.Complete();
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 等待ActionBlock <T> - TPL DataFlow - Awaiting ActionBlock<T> - TPL DataFlow 为什么我的 TPL 数据流操作块没有并行执行? - Why is my TPL Dataflow Actionblock not executing in parallell? 如何将 TPL Dataflow TransformBlock 或 ActionBlock 放在单独的文件中? - How to put a TPL Dataflow TranformBlock or ActionBlock in a separate file? TPL数据流转换块发布到批处理块后跟动作块 - TPL Dataflow Transform block post to batch block followed by actionblock 使用Parallel.ForEach与TPL.Dataflow或其他将OCR应用于大量图像的解决方案 - Using Parallel.ForEach vs TPL.Dataflow or other solution for applying OCR to large number of images Dataflow(TPL)-异常处理问题? - Dataflow(TPL) - exception handling issue? 使用BlockingCollection时出现死锁 <T> 和TPL数据流一起 - Deadlocks when using BlockingCollection<T> and TPL dataflow together 直接指定动作或使用TPL数据流ActionBlock指定任务生成器有什么区别? - What is the difference between specifying Action directly or Task-Generator with TPL dataflow ActionBlock? TPL数据流。 无法将BatchBlock附加到ActionBlock。 无法从用法中推断出类型实参? - TPL Dataflow. Cannot attach BatchBlock to ActionBlock. Type argument cannot be inferred from usage? TPL 数据流:避免在每次调用其委托时重复运行 using 块(例如写入 StreamWriter)的 ActionBlock - TPL Dataflow: ActionBlock that avoids repeatedly running a using-block (such as for writing to a StreamWriter) on every invocation of its delegate
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM