2011-02-09 137 views
1

多线程仍然是我的待办事项列表上,所以标题可能是完全错误的:)如何同步事件处理程序

我的目标是听串口,就像这样:

class MyClass 
{ 
    MyOpticalScanner _scanner; 

    public MyClass() 
    { 
     _scanner = new MyOpticalScanner(); 
     _scanner.CodeScanned += CodeScannedEventHandler; 
    } 

    private void CodeScannedEventHandler(object sender, CodeScannedEventArgs e) 
    { 
     Debug.WriteLine("ThreadID: " + Thread.CurrentThread.ManagedThreadId + " ||| " + e.ScannedCode); 
     .... 
     // Some code, query the database, etc... 
    } 
} 

的扫描仪发送命令数据:001002003004005,......但是,如果在CodeScannedEventHandler代码花费太长时间来处理,然后又引发事件和我不一致的排序。该Debug.WriteLine在事件处理程序可以给我这样的:

ThreadID: 8 ||| 001 
ThreadID: 9 ||| 002 
ThreadID: 10 ||| 003 
ThreadID: 10 ||| 006 
ThreadID: 8 ||| 004 
ThreadID: 8 ||| 008 
ThreadID: 8 ||| 009 
ThreadID: 8 ||| 010 
ThreadID: 10 ||| 007 
ThreadID: 9 ||| 005 

我怎么能保证,每一个新的事件开始,只有当旧的结束来处理?

编辑1 - 我没有告诉你所有的事情,实际上我没有在测试过程中听COM端口,而是我创建了自己的模拟对象。此对象(ScannerMock)使用内部System.Timer并引发Timer.OnTick事件中的CodeScanned事件。问题能在这里吗?

来自Hans Passant's answer中的评论:SerialPort有一个内部锁,可确保DataReceived事件在运行时不能再次被调用。我应该在我的嘲笑扫描仪中包含类似的锁,以及如何?

编辑2:我向我的扫描仪对象添加了锁,并将代码引发了我的事件。它看起来像它的作品:)

+1

为什么你在多个线程中处理值呢?无论如何,您始终可以将其锁定在扫描仪中,因此一次只能触发一个事件。 – 2011-02-09 15:15:01

+0

我不认为问题是'事件处理程序',因为它们不是异步的。 `scan`方法是执行异步的方法,因为事件处理程序来自它,所以看起来他们也是异步的,但它们不是。 – Jaider 2013-01-09 21:12:46

回答

4

如果CodeScannedEventHandler在前一个运行完成之前再次运行,则会遇到相当大的问题。串行端口不是做这个顺便说一句,它的DataReceived事件是序列化的。锁不能可靠地解决这个问题,线程获取锁的顺序不能保证。这就是你看到的情况,Debug.WriteLine()中有一个锁。

如果顺序非常重要,那么您所能做的就是让事件处理程序尽可能简短快捷,因此它总是比事件运行的速度少。将扫描结果快速存储在线程安全队列中并取出。您需要另一个清空队列的线程。它仍然不是100%的保证,你需要任何写MyOpticalScanner的人来获得帮助。

3

我该如何保证,每个新事件开始只有当旧事件完成后才能处理?

如果这样做,您有可能丢失传入数据和/或溢出接收缓冲区。

一种方法可能是将传入数据快速地推送到队列中,然后在另一个线程中处理该队列。如果在可以工作的代码之间有一段时间。

但是,如果消费者无法跟上,你仍然陷入困境。

0

我该如何保证每个新事件只有在旧事件完成时才开始处理?

例如,通过使用lock声明:但是

class MyClass { 
    ... 
    Object myLock = new Object(); 
    ... 

    private void CodeScannedEventHandler(object sender, CodeScannedEventArgs e) 
    { 
     lock(myLock) { 
      ... 
     } 
    } 
} 

注意,这可能解决您的问题:该解决方案将防止并行运行的事件处理程序的两个实例。它将而不是保证当前正在等待的线程以“正确”的顺序被释放。

如果您的扫描器组件支持此功能,那么最简单的解决方案是将该组件配置为始终在同一线程上调用事件处理程序。

0

这是因为

如果您在多个线程中扫描,这当然会发生。

您应该同步CodeScannedEventHandler方法。例如,您可以使用lock

1

我建议你使用一个线程安全的队列类,当添加一个项目时,它会报告自队列上次报告它是否为空后是否添加了另一个项目(将普通队列包装在一个锁中并添加一个'空'旗 - 在锁内 - 应该足够了)。每当通讯记录到达时,将其添加到队列中,如果自上次“空”报告以来没有添加任何内容,则派遣MethodInvoker读取并处理队列中的所有内容,并在队列为空时退出(该方法可以重复调用“收到的一条记录”事件)。

如果一个记录正在处理另一个记录到达时,队列将报告至少有一条记录自从上次队列报告自己为空以来已添加,因此不会派发新的MethodInvoker。任何在队列报告自己为空前排队的记录都将由之前的MethodInvoker处理;任何在MethodInvoker发现队列为空后入队的记录都不会被该MethodInvoker处理,并且需要启动另一个记录。