public class TaskStatesPoller : IDataPoller
{
private Controller controller;
internal static Threader<Controller> Threader = new Threader<Controller>(delegate (Controller controller, DelegateSpooler spooler)
{
controller.ControlCenter.TaskStates.Update(spooler);
}, typeof(TaskStatesPoller).Name);
private object subscriptionMutex = new object();
private volatile bool controllerResetting;
private bool enabled;
private bool autoStart = true;
private EventHandler<NewTaskStatesArrivedEventArgs> newTaskStatesArrivedEventHandler;
private EventHandler<ErrorEventArgs> errorOccurred;
private INamedConstantCollection<TaskStatusData, TaskId> last;
public bool IsSuspended => !enabled;
public bool AutoStart
{
get
{
return autoStart;
}
set
{
autoStart = value;
}
}
public bool IsExecutingEvent => Threader.Spooler.IsPipeExecuting(controller.Number);
public int RefreshInterval
{
get
{
return Threader.Interval;
}
set
{
Threader.Interval = value;
}
}
public NewTaskStatesArrivedEventArgs Latest
{
get
{
if (last == null)
{
return null;
}
return new NewTaskStatesArrivedEventArgs(controller, last);
}
}
public event EventHandler<NewTaskStatesArrivedEventArgs> NewTaskStatesArrived
{
add
{
lock (subscriptionMutex)
{
newTaskStatesArrivedEventHandler = (EventHandler<NewTaskStatesArrivedEventArgs>)Delegate.Combine(newTaskStatesArrivedEventHandler, value);
if (autoStart)
{
Threader.AddSubscriber(controller);
enabled = true;
}
}
}
remove
{
lock (subscriptionMutex)
{
newTaskStatesArrivedEventHandler = (EventHandler<NewTaskStatesArrivedEventArgs>)Delegate.Remove(newTaskStatesArrivedEventHandler, value);
if (newTaskStatesArrivedEventHandler == null || newTaskStatesArrivedEventHandler.GetInvocationList().Length == 0)
{
Threader.RemoveSubscriber(controller, controller.Number);
enabled = false;
}
}
}
}
public event EventHandler<ErrorEventArgs> ErrorOccurred
{
add
{
lock (subscriptionMutex)
{
errorOccurred = (EventHandler<ErrorEventArgs>)Delegate.Combine(errorOccurred, value);
}
}
remove
{
lock (subscriptionMutex)
{
errorOccurred = (EventHandler<ErrorEventArgs>)Delegate.Remove(errorOccurred, value);
}
}
}
internal TaskStatesPoller(Controller controller)
{
this.controller = controller;
this.controller.Information.ControllerResettingPre += delegate
{
controllerResetting = true;
};
this.controller.Information.ControllerResetPost += delegate
{
controllerResetting = false;
};
}
public void Suspend()
{
if (enabled)
{
lock (subscriptionMutex)
{
Threader.RemoveSubscriber(controller, controller.Number);
enabled = false;
}
}
}
public void Resume()
{
if (!enabled)
{
lock (subscriptionMutex)
{
Threader.AddSubscriber(controller);
enabled = true;
}
}
}
void IDataPoller.UnsubscribeAll()
{
lock (subscriptionMutex)
{
if (newTaskStatesArrivedEventHandler != null)
{
Delegate[] invocationList = newTaskStatesArrivedEventHandler.GetInvocationList();
for (int i = 0; i < invocationList.Length; i++)
{
EventHandler<NewTaskStatesArrivedEventArgs> value = (EventHandler<NewTaskStatesArrivedEventArgs>)invocationList[i];
NewTaskStatesArrived -= value;
}
}
if (errorOccurred != null)
{
Delegate[] invocationList = errorOccurred.GetInvocationList();
for (int i = 0; i < invocationList.Length; i++)
{
EventHandler<ErrorEventArgs> value2 = (EventHandler<ErrorEventArgs>)invocationList[i];
errorOccurred = (EventHandler<ErrorEventArgs>)Delegate.Remove(errorOccurred, value2);
}
}
}
}
internal void RaiseErrorOccurred(Exception e)
{
EventHandler<ErrorEventArgs> eventHandler = null;
lock (subscriptionMutex)
{
eventHandler = errorOccurred;
}
eventHandler?.Invoke(this, new ErrorEventArgs(controller, e));
}
internal void RaiseNewTaskStatesArrived(INamedConstantCollection<TaskStatusData, TaskId> taskStatuses)
{
EventHandler<NewTaskStatesArrivedEventArgs> eventHandler;
lock (subscriptionMutex)
{
eventHandler = newTaskStatesArrivedEventHandler;
}
eventHandler?.Invoke(this, new NewTaskStatesArrivedEventArgs(controller, taskStatuses));
}
private void Update(DelegateSpooler spooler)
{
int number = controller.Number;
INamedConstantCollection<TaskStatusData, TaskId> taskStatuses = null;
try
{
taskStatuses = controller.Tasks.GetAllStatusForAllTasks();
}
catch (Exception ex)
{
Exception ex2 = ex;
Exception e = ex2;
spooler.Set(number, delegate
{
controller.ControlCenter.TaskStates.RaiseErrorOccurred(e);
});
return;
}
if (controllerResetting || taskStatuses == null)
{
return;
}
last = taskStatuses;
spooler.Set(number, delegate
{
if (!controllerResetting)
{
RaiseNewTaskStatesArrived(taskStatuses);
}
});
}
}
以下是针对 TaskStatesPoller
类的全面分析,结合架构设计和实现细节:
1. 核心功能
这是一个 任务状态轮询器,实现 IDataPoller
接口,主要职责包括:
-
定期轮询控制器的任务状态(通过
controller.Tasks.GetAllStatusForAllTasks()
) -
线程安全的事件通知机制:
-
NewTaskStatesArrived
:新状态数据到达事件 -
ErrorOccurred
:轮询异常事件
-
-
动态启停控制(暂停/恢复轮询)
2. 关键设计分析
(1)线程模型
-
共享线程管理器
使用静态Threader<Controller>
统一管理轮询线程,避免为每个实例创建独立线程。
internal static Threader<Controller> Threader = new Threader<Controller>(
(controller, spooler) => controller.ControlCenter.TaskStates.Update(spooler),
typeof(TaskStatesPoller).Name
);
-
-
通过委托将实际轮询逻辑委托给
ControlCenter.TaskStates
。
-
-
异步回调队列
通过DelegateSpooler
确保事件在正确线程上下文执行,避免跨线程问题。
(2)状态管理
字段 | 作用 | 线程安全措施 |
---|---|---|
controllerResetting | 标记控制器是否正在重置 | volatile 保证可见性 |
enabled | 轮询启用状态 | lock(subscriptionMutex) |
last | 缓存最后一次状态数据 | 通过属性 Latest 安全访问 |
(3)事件订阅机制
-
自动启停逻辑
public event EventHandler<NewTaskStatesArrivedEventArgs> NewTaskStatesArrived {
add {
if (autoStart) {
Threader.AddSubscriber(controller); // 首个订阅者自动启动
enabled = true;
}
}
remove {
if (无订阅者) {
Threader.RemoveSubscriber(...); // 无订阅者自动停止
enabled = false;
}
}
}
-
-
通过
autoStart
配置是否启用自动订阅管理。
-
-
线程安全的事件操作
所有事件增减操作均通过subscriptionMutex
加锁保护。
3. 核心方法解析
(1)轮询逻辑(Update)
private void Update(DelegateSpooler spooler) {
try {
var taskStatuses = controller.Tasks.GetAllStatusForAllTasks();
if (!controllerResetting && taskStatuses != null) {
last = taskStatuses; // 更新缓存
spooler.Queue(() => RaiseNewTaskStatesArrived(taskStatuses));
}
} catch (Exception e) {
spooler.Queue(() => RaiseErrorOccurred(e)); // 异步通知错误
}
}
-
异常处理:捕获所有异常并通过事件通知调用方。
-
状态检查:忽略控制器重置期间的无效数据。
(2)资源清理
void IDataPoller.UnsubscribeAll() {
lock (subscriptionMutex) {
// 强制移除所有事件订阅者
foreach (var handler in NewTaskStatesArrived.GetInvocationList()) {
NewTaskStatesArrived -= (EventHandler<...>)handler;
}
// 同样清理ErrorOccurred...
}
}
-
实现接口要求,确保无资源泄漏。
4. 线程安全设计
机制 | 应用场景 | 实现方式 |
---|---|---|
锁 | 事件订阅/取消、启停控制 | lock(subscriptionMutex) |
volatile | 控制器重置标志 | volatile bool controllerResetting |
委托队列 | 跨线程事件触发 | DelegateSpooler 异步执行 |
5. 使用示例
var poller = new TaskStatesPoller(controller);
poller.NewTaskStatesArrived += (s, e) => {
Console.WriteLine($"Task {e.TaskStatuses} updated");
};
poller.ErrorOccurred += (s, e) => {
Console.WriteLine($"Error: {e.Exception.Message}");
};
// 手动控制
poller.Suspend();
poller.Resume();
6. 改进建议
-
Disposable 模式
增加IDisposable
实现,确保线程和事件资源释放。 -
取消支持
引入CancellationToken
替代volatile
停止标志。 -
性能优化
-
使用
ConcurrentDictionary
替代lock
管理订阅者(需测竞争条件)。 -
缓存
GetInvocationList()
结果避免重复分配。
-
-
日志增强
记录轮询周期、异常堆栈等调试信息。
总结
该类是一个典型的生产者-消费者模型实现,亮点在于:
-
高效的线程共享:通过静态
Threader
减少线程开销。 -
灵活的事件管理:自动启停与线程安全订阅。
-
稳健的错误处理:异常捕获与异步通知机制。
适用于需要实时监控任务状态的工业控制场景,其设计平衡了性能与线程安全需求。