C# - I / O卡读取期间的异步事件触发

时间:2016-10-10 09:02:19

标签: c# multithreading

我正在为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.Run简单地触发事件:

Task.Run(() => {
    OnBitChanged(new BitChangedEventArgs()
    {
        Port = (byte)i,
        Value = _currentState[i]
    }); 
});

第二种方法是在OnBitChanged方法内调用委托上的BeginInvoke:

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);
    }
}

还有其他想法吗?我走错了路吗?

1 个答案:

答案 0 :(得分:0)

两者几乎相同 - 都将处理程序发布到线程池。所以你最好使用Task.Run,它有一个更好的界面。

最常用的方法是使用某种队列。这样,您就不会浪费超过必要的线程。这可能就像从队列中读取另一个线程并执行处理程序一样简单,也可以是可以与Task.Run一起使用的自定义任务调度程序。线程池本身也是一个队列,但它引入了处理程序的并行性,这可能是不可取的 - 我希望您的应用程序协议可以在单个执行线程中工作。

最后,如果不读取数据会导致数据丢失,即使您将处理程序发布到另一个线程,您仍有机会丢失数据 - 最终,您需要一个可以自行缓冲的API。