ActionBlock永远不会在Faulted State上完成

时间:2017-12-05 06:27:10

标签: c# task-parallel-library tpl-dataflow

我正在尝试制作一个简单的程序来更好地理解TPL Dataflow。我正在尝试创建一个长时间运行的任务,如果已完成,将重新启动数据流块。

如果我通过输入“-1”在块上显式调用complete,我当前的RestartActionBlock任务可以等待ActionBlock的完成。但是当我尝试引发异常以使块出错或调用块Fault()接口方法时,ActionBlock的完成任务永远不会完成。在这种情况下,await singleTestFlow.Completion;呼叫永远不会继续。

在引发异常或调用Fault()方法之后,我可以通过尝试在程序中输入另一个输入并调试代码以查看该块是否处于故障状态来确定该块是否处于故障状态:

ActionBlock in the faulted state

如果块处于故障状态,为什么await singleTestFlow.Completion;永远不会返回?

class Program
{
    private static ActionBlock<string> singleTestFlow;

    static void Main(string[] args)
    {
        //start thread that should restart a completed action block
        Task.Run(RestartActionBlock);

        Console.WriteLine("Enter -0 to exit, -1 to complete flow, -0- to throw an exception, Anything else otherwise");
        var input = Console.ReadLine();

        //allow user to input text until "-0" is entered
        while (!input.Equals("-0"))
        {
            if (input.Equals("-1"))
            {
                singleTestFlow.Complete();
            }

            singleTestFlow.Post(input);

            input = Console.ReadLine();
        }

        async Task RestartActionBlock()
        {
            var iterations = 0;
            while (true)
            {
                singleTestFlow = new ActionBlock<string>(s =>
                {
                    if (s.Equals("-0-"))
                    {
                        //throw new Exception("Something went wrong in here");
                        ((IDataflowBlock)singleTestFlow).Fault(new Exception("Something went wrong in here"));
                    }
                    Console.WriteLine($"{iterations}: " + s);

                }, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = Environment.ProcessorCount });

                await singleTestFlow.Completion;

                var completionTask = singleTestFlow.Completion;

                var message = $"action block: {iterations} ";
                switch (completionTask.Status)
                {
                    case TaskStatus.RanToCompletion:
                        message += "ran to completion";
                        break;
                    case TaskStatus.Canceled:
                        message += "was canceled";
                        break;
                    case TaskStatus.Faulted:
                        message += "has faulted";
                        Console.WriteLine(completionTask.Exception);
                        break;
                }
                Console.WriteLine(message);
                iterations++;
            }
        }
    }


}

Sample Console Entry 我输入“eee”的控制台中的点是故障块调试屏幕截图的位置。

1 个答案:

答案 0 :(得分:2)

这是一个简单的解决方法:

替换

await singleTestFlow.Completion; // Throws if Completion is faulted, breaking your loop.

await Task.WhenAny(singleTestFlow.Completion);

await singleTestFlow.Completion.ContinueWith(_ => { });

以上操作是避免传播Completion任务异常。这允许您的RestartActionBlock循环按预期永久执行(而不是在块故障后立即死亡)。

但是,理想情况下,您不应忽视以下两个语句的返回值:

Task.Run(RestartActionBlock); // Unobserved Task. Usually a bad idea.

singleTestFlow.Post(input);

如果您正在观察上述的返回值,您实际上会注意到,一旦ActionBlock<string>出现故障,他们就会开始大声寻求帮助。

Task.Run(RestartActionBlock)一旦Faulted抛出,await singleTestFlow.Completion返回的任务就会转换为ActionBlock<string>状态 - 但是您永远不会得到通知,因为您没有保留对所述任务的引用,或者检查它的地位。

同样,在您的singleTestFlow.Post(input)错误发生之后,对false的后续调用实际上会返回window.location.href,这意味着不会有更多项目发布到该区块。他们被丢弃了。