乐于分享
好东西不私藏

设备软件中将方法调用封装为命令对象的ActiveObject类讲解

设备软件中将方法调用封装为命令对象的ActiveObject类讲解

一、代码实现

using System;using System.Threading;using Cimetrix.Library;using Cimetrix.Library.Logging;namespace Cimetrix.CimControlFramework.Common.Commands{    ///<summary>    /// Simple implementation of the ActiveObject design pattern    ///</summary>    public class ActiveObject : IInvokerIDisposable    {        #region Constructors        ///<summary>        /// Static constructor        ///</summary>        staticActiveObject()        {            DefaultServicePeriod = TimeSpan.FromSeconds(0.200);        }        ///<summary>        /// Instance constructor        ///</summary>        ///<param name="name">Name</param>        publicActiveObject(string name)        {            ifstring.IsNullOrEmpty(name) )                throw new ArgumentException("name cannot be null or empty""name");            Name = name;            ThreadEvent = new AutoResetEvent(false);            CommandQueue = new CommandQueue(ThreadEvent);            NominalServicePeriod = DefaultServicePeriod;        }        #endregion        #region Types        #endregion        #region Fields        Thread thread;        volatile bool stop;        volatile bool processingCommand;        bool isDisposed;        #endregion        #region Properties        ///<summary>        /// Gets the name        ///</summary>        public string Name { getprivate set; }        ///<summary>        /// Gets or sets the service period        ///</summary>        public TimeSpan NominalServicePeriod { getset; }        ///<summary>        /// Gets the ThreadEvent        ///</summary>        public EventWaitHandle ThreadEvent { getprivate set; }        ///<summary>        /// Gets flag indicating if there are commands (true) or not        ///</summary>        public bool HasCommands        {            get { return processingCommand || CommandQueue.Count > 0; }        }        ///<summary>        /// Gets or sets the default value for <see cref="NominalServicePeriod"/>        ///</summary>        public static TimeSpan DefaultServicePeriod { getset; }        ///<summary>        /// Gets the command queue        ///</summary>        protected CommandQueue CommandQueue { getprivate set; }        ///<summary>        /// Delegate called when the state should be updated.        ///</summary>        public Action ServiceState { getset; }        ///<summary>        /// Delegate which is called immediately prior to invoking command        ///</summary>        public Action<Command> InvokingCommand { getset; }        ///<summary>        /// Delegate which is called immediately after invoking command        ///</summary>        public Action<Command> InvokedCommand { getset; }        #endregion        #region Methods        #region IDisposable Members        ///<summary>        /// Clean up any resources being used.        ///</summary>        ///<param name="disposing">true if managed resources should be disposed; otherwise, false.</param>        protectedvirtualvoidDispose(bool disposing)        {            if( disposing && isDisposed == false )            {                // Dispose of managed resources                Stop();                if( ThreadEvent != null )                {                    ThreadEvent.Close();                    CxObject.DisposeIfNeeded(ThreadEvent);                    ThreadEvent = null;                }                isDisposed = true;            }        }        ///<summary>        /// Dispose of unmanaged resources, both directly and indirectly owned.  Normally called when an object        /// is no longer needed.        ///</summary>        publicvoidDispose()        {            Dispose(true);            GC.SuppressFinalize(this);        }        #endregion        ///<summary>        /// Submits a command to be invoked        ///</summary>        ///<param name="command">Command to be invoked</param>        ///<returns>ICommandProxy</returns>        public ICommandProxy Submit(Command command)        {            return CommandQueue.Enqueue(command);        }        ///<summary>        /// Starts command processing        ///</summary>        publicvirtualvoidStart()        {            if( isDisposed == true || thread != null )                return;            thread = new Thread(ThreadMonitor.Create(Run)) { IsBackground = true, Name = Name };            thread.Start();        }        ///<summary>        /// Stops command processing        ///</summary>        publicvirtualvoidStop()        {            if( isDisposed == true )                return;            stop = true;            ifnull == thread || !thread.IsAlive )                return;            ThreadEvent.Set();            if( Thread.CurrentThread == thread )                return;            // Don't allow infinite wait in Dispose or Finalizer.            if( thread.Join(5000) == false )                thread.Abort(); // Abort the thread rather than let it run in a Disposed object.            thread = null;        }        ///<summary>        /// Home of the ActiveObject thread        ///</summary>        protectedvirtualvoidRun()        {            try            {                PrepareToRunActiveObject();                while( stop == false )                {                    ServiceCommands();                    Action temp = ServiceState;                    if( temp != null )                        temp();                    ThreadEvent.WaitOne(NominalServicePeriod, false);                }                CompleteRunningActiveObject();            }            catch( Exception ex )            {                //catch and log the exception so the app doesn't die                Log.WriteExceptionCatch(Name, ex);            }        }        ///<summary>        /// Called by <see cref="ActiveObject"/> thread before entering command loop        ///</summary>        protectedvirtualvoidPrepareToRunActiveObject() { }        ///<summary>        /// Called by <see cref="ActiveObject"/> thread after leaving command loop        ///</summary>        protectedvirtualvoidCompleteRunningActiveObject() { }        ///<summary>        /// Called by <see cref="ActiveObject"/> thread to execute a command        ///</summary>        ///<param name="command">Command</param>        protectedvirtualvoidProcessCommand(Command command)        {            try            {                Action<Command> temp = InvokingCommand;                if( temp != null )                    temp(command);                command.Invoke();                temp = InvokedCommand;                if( temp != null )                    temp(command);            }            catch( Exception e )            {                Log.WriteExceptionCatch(Name, e);                Log.WriteIfEnabled(LogCategory.Debug, Name, "Caught exception during command {0}", command.Description);                // Make sure command is completed                string errorCode = String.Format("Encountered exception \"{0}\" invoking command.", e);                command.NoteCommandCompleted(errorCode);            }        }        ///<summary>        /// Called by <see cref="ActiveObject"/> thread to execute all queued commands        ///</summary>        protectedvirtualvoidServiceCommands()        {            if( CommandQueue.Count == 0 )                return;            processingCommand = true;            try            {                Command command;                whilenull != (command = CommandQueue.Dequeue()) )                    ProcessCommand(command);            }            finally            {                processingCommand = false;            }        }        ///<summary>        /// Gets if ActiveObject is currently alive        ///</summary>        ///<returns>True if alive, otherwise false</returns>        publicvirtualboolIsAlive()        {            return thread != null && thread.IsAlive;        }        #endregion    }}

二、代码概述

上述代码实现了一个 ActiveObject(主动对象) 设计模式的基础框架。

该模式通过将方法调用封装为命令对象,并交给专用线程排队执行,实现了调用与执行的分离,从而简化多线程编程,避免显式锁同步。

ActiveObject 类实现了 IInvoker 和 IDisposable,主要职责包括:

  • 维护一个后台线程,循环处理命令队列;
  • 支持定期的“状态更新”回调(ServiceState);
  • 提供命令提交、启动/停止、资源释放等完整生命周期管理;
  • 内置异常捕获与日志,保证线程不因单次错误而终止。

三、设计亮点

1. 主动对象模式的核心抽象

  • 任务抽象为 Command 类,通过 Submit 入队,由专用线程顺序执行,天然避免竞态条件。
  • 使用 CommandQueue(内部线程安全队列)与 AutoResetEvent 结合,实现高效的生产者-消费者模型。

2. 可配置的服务周期

  • NominalServicePeriod
     允许设置空闲时定期执行 ServiceState 委托,适合轮询状态、定时任务等场景。
  • DefaultServicePeriod
     静态属性可全局调整默认周期。

3. 完整的生命周期钩子

  • PrepareToRunActiveObject
     / CompleteRunningActiveObject 虚方法:派生类可在线程循环前后执行初始化/清理。
  • ServiceState
    InvokingCommandInvokedCommand 委托:灵活插入业务逻辑,无需继承。

4. 健壮的线程管理

  • volatile bool stop
     与 ThreadEvent.Set() 协作,优雅通知线程退出。
  • Stop()
     方法先尝试 Join,超时后调用 Abort(),避免资源泄漏或死锁。
  • 通过 isDisposed 标志防止重复释放。

5. 异常隔离

  • Run()
     外层 try-catch:捕获所有未处理异常,防止后台线程意外终止。
  • ProcessCommand()
     内层 try-catch:单条命令异常仅记录日志并标记命令失败,不影响后续命令。

6. 资源管理规范

  • 实现 IDisposable 模式,正确释放 EventWaitHandle、中止线程。
  • 调用 CxObject.DisposeIfNeeded 兼容 Cimetrix 框架的通用资源释放。

7. 可扩展性

  • 大部分方法标记为 protected virtual,派生类可重写 RunServiceCommandsProcessCommand 等,完全自定义行为。

四、使用方式(伪代码)

1. 基本用法

// 创建主动对象,指定线程名称var activeObj = new ActiveObject("Worker");// 可选:设置服务周期(200ms 已为默认)activeObj.NominalServicePeriod = TimeSpan.FromMilliseconds(500);// 可选:注册状态更新回调(每次循环执行)activeObj.ServiceState = () => {    Console.WriteLine("周期性状态检查...");};// 启动后台线程activeObj.Start();// 提交命令(同步等待结果示例)Command cmd = new MyCommand();ICommandProxy proxy = activeObj.Submit(cmd);// 可通过 proxy.Wait() 阻塞等待完成(假设 ICommandProxy 提供)// 停止并释放activeObj.Stop();activeObj.Dispose();

2. 自定义命令类

public class PrintCommand : Command{    private string _message;    publicPrintCommand(string msg) => _message = msg;    publicoverridevoidInvoke()    {        Console.WriteLine(_message);        // 模拟耗时操作        Thread.Sleep(100);        // 通知完成(Command 基类应有机制)        NoteCommandCompleted(null);    }}// 使用activeObj.Submit(new PrintCommand("Hello Active Object"));

3. 派生类扩展(重写生命周期方法)

public class MyActiveObject : ActiveObject{    publicMyActiveObject(string name) : base(name) { }    protectedoverridevoidPrepareToRunActiveObject()    {        // 打开数据库连接、初始化资源等    }    protectedoverridevoidCompleteRunningActiveObject()    {        // 关闭连接、清理资源    }    protectedoverridevoidProcessCommand(Command command)    {        // 添加日志或性能统计        base.ProcessCommand(command);    }}

4. 使用委托拦截命令

activeObj.InvokingCommand = (cmd) => {    Log.Info($"即将执行命令: {cmd.Description}");};activeObj.InvokedCommand = (cmd) => {    Log.Info($"命令执行完毕: {cmd.Description}");};

5. 集成到大型系统(如 CIM 框架,设备软件)

// 将 ActiveObject 作为设备控制器的后台任务处理器public class EquipmentController : IDisposable{    private ActiveObject _worker;    publicEquipmentController()    {        _worker = new ActiveObject("EQController");        _worker.ServiceState = PollEquipmentStatus;        _worker.Start();    }    publicvoidMoveStage(int x, int y)    {        _worker.Submit(new MoveCommand(x, y));    }    privatevoidPollEquipmentStatus()    {        // 读取硬件状态,触发事件等    }    publicvoidDispose() => _worker.Dispose();}

五、总结

ActiveObject 类是一个轻量级、高内聚的并发执行引擎,完美体现了主动对象设计模式的精髓。

它将线程管理命令调度异常处理扩展点有机整合,既能作为独立的后台任务处理器,也能作为框架基类被继承定制。

该设计在工业自动化(如 SEMI 标准设备控制)、GUI 后台任务、定时轮询等场景中具有很高的实用价值。

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » 设备软件中将方法调用封装为命令对象的ActiveObject类讲解

评论 抢沙发

7 + 6 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
×
订阅图标按钮