private List<byte> RevBuffer = new List<byte>();private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
    lock (RevBuffer)
    {
        var buffer = new byte[4000];
        var len = 0;
        while ((len = serialPort1.Read(buffer, 0, buffer.Length)) > 0)
            RevBuffer.AddRange(buffer.Take(len));
        ThreadPool.QueueUserWorkItem(h =>         //不等ProcessCommand执行完,立即重新读取串口新数据
        {
            while (ProcessCommand()) ;      //处理收到的所有消息,直到再没有任务
        });
    }
}private bool ProcessCommand()
{
    lock (RevBuffer)
    {
        var len = 查找一个命令的长度(RevBuffer);
        if (len == 0)      //不包含任何完整的任务(比如说还没有接受到第一个消息的结束标志
            return false;        var command = new byte[len];
        Array.Copy( RevBuffer.ToArray() , command, len );
        RevBuffer.RemoveRange(0, len );       //从接收缓冲区移除第一个消息内容
        ThreadPool.QueueUserWorkItem(h => 执行一条命令(command));
        return true;    //通知调用程序,收到了一个任务
    }
}

解决方案 »

  1.   

    ProcessCommand完成得很快(通常只有几毫秒),所以不会卡住读取线程。不需要等待命令执行,就开始读取新的数据。命令使用(系统系统线程池上的)子线程并发执行。
      

  2.   


    所有的线程都可以不开。实际的系统是为了性能和用户(UI交互时)操作体验而设计的。如果你只是完成基本逻辑,永远也不用考虑使用线程的。所以说,如果你在“还看不懂基本逻辑”的时候,你只要把 ThreadPool.QueueUserWorkItem(...)  这句话外边的“壳儿”去掉、仅留下里边的代码,就行了。
      

  3.   

    所有的线程都可以不开。实际的系统是为了性能和用户(UI交互时)操作体验而设计的。如果你只是完成基本逻辑,永远也不用考虑使用线程的。所以说,如果你在“还看不懂基本逻辑”的时候,你只要把 ThreadPool.QueueUserWorkItem(...)  这句话外边的“壳儿”去掉、仅留下里边的代码,就行了。你只要把 ThreadPool.QueueUserWorkItem(...)  这句话外边的“壳儿”去掉、是把ProcessCommand()函数去掉吗,看不太懂啊
      

  4.   

    感谢上面那位大大,虽然没看太懂,主要是我水平未够。经过努力,我已经做成功了。下面分享下:
    1、对于第一个问题:
    首先定义全局变量List <byte> list =new List<byte>() //必须定义为全局
    int b=0
              
    然后在private void comm_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
    {
              int a=comm.ByteToRead;
              byte []buf=new byte[a];
              comm.Read(buf,0,a);   
              for(i=0;i<a;i++)//用来将buf的数据拼接到list数组,以保证把完整的一帧数据加到一个数组里面
               {
                       list.Insert(b,buf[i]);
                       b++;
                   }  
               if(list[list.account-1]==0xD0&&list[list.account-2]==0xDE)//判断数据是否收完;DE和D0为帧结束符在下面处理数据
               {
                  对于第2个问题,利用LIST.Removerange(int ,length)//在指定索引处删除一定的长度
                 if(list[2]==0x08)
                   {
                  做相应处理;
                   list.removerange(0,6);                }
                   if(list[2]==0x55)
                   {
                  做相应处理;
                   list.removerange(0,6);                }
                   if(list[2]==0x66)
                   {
                  做相应处理;
                          }
                list.Clear();//清空list用来下次接收
                b=0;
               }
               
    }                 
    详细的根据自己的通信协议按这个框架应该是可以的。
    分享下,csdn就是分享知识的社区,yeah!
      

  5.   

    接收到的信息(list中的内容),不一定只有一个命令(例如发消息的设备很多、发送速度远比上位机处理速度快),可能有多2个甚至多个。你的程序是假设每一次接收到“一个”命令,只执行一次命令就结束。你的程序并不能保证正确,只是说你现在的测试环境“恰好”设备比较慢、而双绞线距离比较短、主机比较空闲,因此基本上都能恰好每当设备发来一条消息时能够满足 list[list.account-1]==0xD0&&list[list.account-2]==0xDE 这个条件。如果你能兼容“收到多条命令”的形式(甚至你能发挥想象力先来制造一个此类测试,然后再解决bug),程序将来用到实际的各种生产环境中才会比较可靠。在你的程序中,既然执行 list.Clear(),那么前边其实就不需要执行 list.removerange(0,6)。 但是list.Cear()其实是不对的,因此最终还是应该保留 list.removerange 而删掉 list.Celar。
    另外将来让程序变成一个教正规、可为中型应用而扩展的“设计升级”的建议:1. DataReceive中的程序,应该清晰地是处理这个Receive流程的。你应该把判断任务类型和调用处理程序的代码,从这段程序中分离出去,不应该在这里出现一堆的 if...else 判断。
    2. 将来还是要在子线程中执行命令解析和调用处理程序过程,而 Receive程序应该仅仅在从list中去掉一条条命令之后,立刻返回(不等命令执行就返回)。以免造成通讯延迟、甚至程序死锁。
      

  6.   

    关于list.removerange(0,6)和 list.Celar()。是这样的。因为,收到的数据的长度是不固定的,有可能是AA AA 08 56 82 44 DE DO也可能是AA AA 08 56 82 44 AA AA 55 63 1A 55 DE D0因为我每次判断的都是list[2],所以判断完08要把AA AA 08 56 82 44这段删掉,才能用list[2]来继续判断list[2]是否为55;
    另外,ifelse这个处理速度应该很快吧。你指的意思是在Receive程序里面开启一个线程吗,在这个线程里用来处理数据吗。