设备软件中将方法调用封装为命令对象的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 : IInvoker, IDisposable{#region Constructors///<summary>/// Static constructor///</summary>staticActiveObject(){DefaultServicePeriod = TimeSpan.FromSeconds(0.200);}///<summary>/// Instance constructor///</summary>///<param name="name">Name</param>publicActiveObject(string name){if( string.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 FieldsThread thread;volatile bool stop;volatile bool processingCommand;bool isDisposed;#endregion#region Properties///<summary>/// Gets the name///</summary>public string Name { get; private set; }///<summary>/// Gets or sets the service period///</summary>public TimeSpan NominalServicePeriod { get; set; }///<summary>/// Gets the ThreadEvent///</summary>public EventWaitHandle ThreadEvent { get; private 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 { get; set; }///<summary>/// Gets the command queue///</summary>protected CommandQueue CommandQueue { get; private set; }///<summary>/// Delegate called when the state should be updated.///</summary>public Action ServiceState { get; set; }///<summary>/// Delegate which is called immediately prior to invoking command///</summary>public Action<Command> InvokingCommand { get; set; }///<summary>/// Delegate which is called immediately after invoking command///</summary>public Action<Command> InvokedCommand { get; set; }#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 resourcesStop();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;if( null == 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 dieLog.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 completedstring 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;while( null != (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
、 InvokingCommand、InvokedCommand委托:灵活插入业务逻辑,无需继承。
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,派生类可重写Run、ServiceCommands、ProcessCommand等,完全自定义行为。
四、使用方式(伪代码)
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 后台任务、定时轮询等场景中具有很高的实用价值。

夜雨聆风
