在读取I/O卡期间触发异步事件
本文关键字:异步 事件 读取 | 更新日期: 2023-09-27 18:12:15
我正在为I/O卡创建包装器。我有一个用于访问I/O卡的DLL库。目前,我的类正在使用这个DLL来打开卡,并读取/写入它的通道。我使用while循环来连续读取输出端口,如下所示:
while (_enabled)
{
BitArray _currentState = ReadCurrentDiValue(); // read DI data with DLL
for (int i = 0; i < 8; i++)
{
if (_cardState[i] != _currentState[i])
{
_cardState[i] = _currentState[i];
OnBitChanged(new BitChangedEventArgs()
{
Port = (byte)i,
Value = _currentState[i]
}); // I want to fire this event asynchronously
}
}
}
这个循环在一个分离的线程上运行。我的问题是,通过在事件处理程序中执行任何困难的工作,事件侦听器可能会阻止该线程运行。在这种情况下,可能传入I/O卡的物理信号不会被识别。问题是,解决这个问题的正确方法是什么?我有两个想法。第一种方法是使用Task简单地触发事件。运行:
Task.Run(() => {
OnBitChanged(new BitChangedEventArgs()
{
Port = (byte)i,
Value = _currentState[i]
});
});
第二种方法是调用委托上的BeginInvoke,在OnBitChanged方法中:
if (BitChanged != null)
{
var listeners = BitChanged.GetInvocationList();
foreach (var listener in listeners)
{
var eventHandler = (EventHandler) listener;
eventHandler.BeginInvoke(this, eventargs, ar =>
{
try
{
eventHandler.EndInvoke(ar);
}
catch
{
// ignore
}
}, null);
}
}
还有其他想法吗?我走错路了吗?
这两种方法几乎完全相同——都将把处理程序发送到线程池。所以你最好使用Task.Run
,它有一个更好的接口。
Task.Run
一起使用的自定义任务调度器。线程池本身也是一个队列,但它为处理程序引入了并行性,这可能是不希望的——我希望您的应用程序协议期望在单个线程执行中工作。
最后,如果不读取数据会导致数据丢失,即使您将处理程序发布到另一个线程,您仍然有可能丢失数据-最终,您需要一个可以自己缓冲的API。