故障后是否可以重置TPL数据流动作块?

时间:2018-04-04 13:29:47

标签: task-parallel-library action tpl-dataflow faulted

我有一个TPL数据流动作块,我用它来接收摄像机的触发消息,然后进行一些处理。如果处理任务抛出异常,则ActionBlock进入故障状态。我想向我的UI发送故障消息并向ActionBlock发送重置消息,以便它可以继续处理传入的触发消息。有没有办法将ActionBlock恢复到就绪状态(清除故障)?

好奇的代码:

using System.Threading.Tasks.Dataflow;

namespace Anonymous
{
    /// <summary>
    /// Provides a messaging system between objects that inherit from Actor
    /// </summary>
    public abstract class Actor
    {
        //The Actor uses an ActionBlock from the DataFlow library.  An ActionBlock has an input queue you can 
        // post messages to and an action that will be invoked for each received message.

        //The ActionBlock handles all of the threading issues internally so that we don't need to deal with 
        // threads or tasks. Thread-safety comes from the fact that ActionBlocks are serialized by default. 
        // If you send two messages to it at the same time it will buffer the second message until the first 
        // has been processed.
        private ActionBlock<Message> _action;

        ...Properties omitted for brevity...

        public Actor(string name, int id)
        {
            _name = name;
            _id = id;
            CreateActionBlock();
        }

        private void CreateActionBlock()
        {
            // We create an action that will convert the actor and the message to dynamic objects 
            // and then call the HandleMessage method.  This means that the runtime will look up 
            // a method called ‘HandleMessage’ with a parameter of the message type and call it.

            // in TPL Dataflow if an exception goes unhandled during the processing of a message, 
            // (HandleMessage) the exception will fault the block’s Completion task.

            //Dynamic objects expose members such as properties and methods at run time, instead 
            // of at compile time. This enables you to create objects to work with structures that 
            // do not match a static type or format. 
            _action = new ActionBlock<Message>(message =>
            {
                dynamic self = this;
                dynamic msg = message;
                self.HandleMessage(msg); //implement HandleMessage in the derived class
            }, new ExecutionDataflowBlockOptions
            {
                MaxDegreeOfParallelism = 1  // This specifies a maximum degree of parallelism of 1.
                                            // This causes the dataflow block to process messages serially.
            });
        }

        /// <summary>
        /// Send a message to an internal ActionBlock for processing
        /// </summary>
        /// <param name="message"></param>
        public async void SendMessage(Message message)
        {
            if (message.Source == null)
                throw new Exception("Message source cannot be null.");
            try
            {
                _action.Post(message);
                await _action.Completion;
                message = null;
                //in TPL Dataflow if an exception goes unhandled during the processing of a message, 
                // the exception will fault the block’s Completion task.
            }
            catch(Exception ex)
            {
                _action.Completion.Dispose();
                //throw new Exception("ActionBlock for " + _name + " failed.", ex);
                Trace.WriteLine("ActionBlock for " + _name + " failed." + ExceptionExtensions.GetFullMessage(ex));

                if (_action.Completion.IsFaulted)
                {
                    _isFaulted = true;
                    _faultReason = _name + " ActionBlock encountered an exception while processing task: " + ex.ToString();
                    FaultMessage msg = new FaultMessage { Source = _name, FaultReason = _faultReason, IsFaulted = _isFaulted };
                    OnFaulted(msg);
                    CreateActionBlock();
                }
            }
        }

        public event EventHandler<FaultMessageEventArgs> Faulted;
        public void OnFaulted(FaultMessage message)
        {
            Faulted?.Invoke(this, new FaultMessageEventArgs { Message = message.Copy() });
            message = null;
        }

        /// <summary>
        /// Use to await the message processing result
        /// </summary>
        public Task Completion
        {
            get
            {
                _action.Complete();
                return _action.Completion;
            }
        }
    }
}

1 个答案:

答案 0 :(得分:0)

ActionBlock中的未处理异常就像应用程序中的未处理异常。不要这样做。适当处理例外情况。

在最简单的情况下,记录它或在块的委托中执行某些操作。在更复杂的场景中,您可以使用TransformBlock而不是ActionBlock,并将Succes或Failure消息发送到下游块。

您发布的代码有一些关键问题。数据流块不是代理,代理不是数据流块。当然,您可以使用其中一个构建另一个,但它们代表不同的范例。在这种情况下,您的Actor会模拟ActionBlock自己的API,但会出现多个错误。

例如,您不需要创建SendAsync,块已经有了Complete()。发送邮件后,您应完成阻止。您无法处理任何其他消息。当您真的不想再使用ActionBlock时,只能致电void MyMethod(MyMessage message) { try { //... } catch(Exception exc) { //ToString logs the *complete exception, no need for anything more _log.Error(exc.ToString()); } } var blockOptions new ExecutionDataflowBlockOptions { BoundedCapacity=10, NameFormat="Block for MyMessage {0} {1}" }; var block=new ActionBlock<MyMessage>(MyMethod,blockOptions); for(int i=0;i<10000;i++) { //Will await if more than 10 messages are waiting await block.SendAsync(new MyMessage(i); } block.Complete(); //Await until all leftover messages are processed await block.Completion; 。您需要将DOP设置为1,这是默认值。

您可以将边界设置为DataflowBlock,以便一次只接受10条消息。否则,所有消息都将被缓冲,直到块找到了处理它们的机会

您可以使用以下代码替换所有代码:

Exception.ToString()

请注意对NameFormat的通话。这将生成一个包含所有异常信息的字符串,包括调用堆栈。

.phases { /*width: 960px;*/ } .breadcrumb_wrapper { color: white; text-decoration: none; padding: 20px 10px 20px 20px; background: blue; /* fallback color */ background: green; position: relative; display: block; float: left; } .breadcrumb_wrapper ul { list-style: none; } .breadcrumb { list-style: none; overflow: hidden; font: 18px Sans-Serif; } .breadcrumb li { float: left; } .breadcrumb li a { color: white; text-decoration: none; padding: 20px 10px 20px 60px; background: blue; /* fallback color */ background: #004c89; position: relative; display: block; float: left; } .breadcrumb li.active a { background: #0078d7; } .breadcrumb li.active a::after { border-left: 30px solid #0078d7; } .breadcrumb li a::after { content: " "; display: block; width: 0; height: 0; border-top: 50px solid transparent; /* Go big on the size, and let overflow hide */ border-bottom: 50px solid transparent; border-left: 30px solid #004c89; position: absolute; top: 50%; margin-top: -50px; left: 100%; z-index: 2; } .breadcrumb li a::before { content: " "; display: block; width: 0; height: 0; border-top: 50px solid transparent; border-bottom: 50px solid transparent; border-left: 30px solid white; position: absolute; top: 50%; margin-top: -50px; margin-left: 5px; left: 100%; z-index: 1; } .breadcrumb li:first-child a { padding-left: 10px; } .breadcrumb li a { background: #004c89; } .breadcrumb li a:after { border-left-color: #004c89; } /* .breadcrumb li:nth-child(2) a { background: hsla(34,85%,45%,1); } .breadcrumb li:nth-child(2) a:after { border-left-color: hsla(34,85%,45%,1); } .breadcrumb li:nth-child(3) a { background: hsla(34,85%,55%,1); } .breadcrumb li:nth-child(3) a:after { border-left-color: hsla(34,85%,55%,1); } .breadcrumb li:nth-child(4) a { background: hsla(34,85%,65%,1); } .breadcrumb li:nth-child(4) a:after { border-left-color: hsla(34,85%,65%,1); } .breadcrumb li:nth-child(5) a { background: hsla(34,85%,75%,1); } .breadcrumb li:nth-child(5) a:after { border-left-color: hsla(34,85%,75%,1); } */ .breadcrumb li a:hover { background: #0078d7; } .breadcrumb li a:hover:after { border-left-color: #0078d7 !important; } .first-set-li{ float:left; margin-right:50px; overflow:hidden; padding-right:33px; margin-top:20px; } .breadcrumb_wrapper ul{ padding:0px; float:left; overflow:hidden; padding-right:40px;} .breadcrumb .breadcrumb_wrapper li a::before{ border-left: 30px solid green; } 允许您为块指定一个名称模板,该模板可以由运行时使用块的内部名称和任务ID填充。