skystalker 发表于 2012-6-9 17:59:48

C#串口通信CPU占用问题

我使用C#编写了一个串口通信上位机 使用serialport控件,数据量大约6.8k/s,115200波特率

调试的时候发现CPU占用比较高,我怀疑是解包占用的时间就把那部分代码注释掉,没有改善

后来做了干脆新建了个项目,只设置串口后打开,不做任何其他处理,CPU占用仍然很高有25%,如果下位机停止发数据,CPU就降下来了。

private void MenuConnect_Click(object sender, RoutedEventArgs e)
      {
            try
            {
                comPort.BaseStream.BaudRate = “115200”;
            }
            catch { }
            comPort.BaseStream.DataBits = 8;
            comPort.BaseStream.StopBits = (StopBits)Enum.Parse(typeof(StopBits), "1");
            comPort.BaseStream.Parity = (Parity)Enum.Parse(typeof(Parity), "None");

         

                comPort.BaseStream.Open();
            }
            catch { }
      }


我觉得是不是serialport类只要有数据来就处理 datareceive之类的事件, 导致了CPU占用很高,以前用C+API写串口数据,做同样的工作只点用3-4%的CPU

请有经验的DX指导下

zxq6 发表于 2012-6-9 18:19:14

你应该用多线程。否则不好弄。

ka_sdk 发表于 2012-6-9 18:49:28

直接运行编译好的程序试试看,调试时CPU占用率自然会高些

shell.albert 发表于 2012-6-9 18:59:22

你打开了串口,虽然你的程序没有读取数据,但操作系统底部--驱动程序却在工作,一直接收数据,放在缓冲区中,这样就导致占用CPU了。明白不?

windboy 发表于 2012-6-9 20:08:27

你是在vs.net 调试环境下运行的吧!

你单独运行一下看看,如果.net程序真这么不济的话,那还用他做几千上万的设备服务的服务器不是把服务器要配置好高才行啊

skystalker 发表于 2012-6-9 20:26:28

windboy 发表于 2012-6-9 20:08 static/image/common/back.gif
你是在vs.net 调试环境下运行的吧!

你单独运行一下看看,如果.net程序真这么不济的话,那还用他做几千上万 ...

在realese模式下单独运行了 也是一样的 我在网上下了N个串口程序 只要使用自带的serialport控件都是一样的效果

skystalker 发表于 2012-6-10 01:14:44

zxq6 发表于 2012-6-9 18:19 static/image/common/back.gif
你应该用多线程。否则不好弄。

是用了多线程

skystalker 发表于 2012-6-10 01:16:19

shell.albert 发表于 2012-6-9 18:59 static/image/common/back.gif
你打开了串口,虽然你的程序没有读取数据,但操作系统底部--驱动程序却在工作,一直接收数据,放在缓冲区中 ...

我也觉得是serialport封装了一些我们看不到的东西在占用CPU

不过别人用QT写的程序没这个问题 CPU占用一直是1-2%

不知C#有什么好的解决办法

hd12 发表于 2012-6-10 02:26:53

会不会你的串口收到干扰了,导致电脑一直认为有数据接受。实验室里有个开关电源一开启,跟电脑连接串口,电脑就会CPU占用100%,拔掉串口或关掉开关电源就好了,你看看是不是类似原因

skystalker 发表于 2012-6-10 12:44:32

hd12 发表于 2012-6-10 02:26 static/image/common/back.gif
会不会你的串口收到干扰了,导致电脑一直认为有数据接受。实验室里有个开关电源一开启,跟电脑连接串口,电 ...

谢谢提醒 我的下位机串口确实是一直在发数据 5ms发一包 一包30个字节左右 每秒数据量6-8K

我觉得应该是如上面网友所说 serialport自己封装了一些操作 导致资源占用过多

大家觉得这咱情况有必要换USB吗?我打算用USB HID

vivalite 发表于 2012-6-10 13:41:07

我觉得是.NET本身的性能问题,串口读写+数据处理我一般用MFC写,然后C#做界面PInvoke进去读数据处理结果,效率会提高很多。

.NET的串口控件微软相当不重视,性能低下,最好只用.NET做界面而由MFC等来做数据采集和处理。

我有个工控需要高速传输数据的项目直接采用网线和电脑连接,传输速率一点不比USB差,还能通过路由器交换机等无限扩张节点数目,编程方面也简单得多。

windboy 发表于 2012-6-11 11:51:34

关于cpu占用这点,我还真没注意过
有空我好好测试一下

windboy 发表于 2012-6-11 12:01:20

我刚才做了一个小测试
用了SerialPort类,打开串口,并 500 ms向串口发送一次数据

用的是Debug单独运行,cpu占用没有

我的是 Vs2010做的

skystalker 发表于 2012-6-18 13:56:25

windboy 发表于 2012-6-11 12:01 static/image/common/back.gif
我刚才做了一个小测试
用了SerialPort类,打开串口,并 500 ms向串口发送一次数据



5ms发一包再试试 每包三十字节

skystalker 发表于 2012-6-18 13:57:24

gamethink 发表于 2012-6-11 12:08 static/image/common/back.gif
不会吧,我的程序运行后,打开串口什么都不干,CPU上升3%左右而已

能说说你的开发环境吗?串口是自己实现的类还是用了什么现成的?

windboy 发表于 2012-6-18 16:02:28

再次按你们所说的测试,没有出现你们所说的情况
我这里 vs2010, debug单独运行 线程:17 CPU:0 内存:15060

附件是我的测试工程代码,请自行测试

skystalker 发表于 2012-6-19 08:59:38

本帖最后由 skystalker 于 2012-6-19 09:01 编辑

windboy 发表于 2012-6-18 16:02 static/image/common/back.gif
再次按你们所说的测试,没有出现你们所说的情况
我这里 vs2010, debug单独运行 线程:17 CPU:0 内存:15060
...

你好 非常感谢你的测试

我看了你的代码 看到里边用发数据进行测试 不过前面我说的是串口打开有接收时CPU占用率高。 另外用timer定时即使发送也是达不到200HZ的 我测试不管INTERVAL多小 一般只能到60HZ

我修改了一下并测试了你的项目,结果是一样的CPU占用20%,见截图:



附件是修改后的项目 大家集思广益 研究下怎么在.net下弄出高性能的串口来吧。我在VC+WIN API环境下是实现过的,具体见我以前发的帖子。

usingavr 发表于 2012-6-19 09:25:31

framework有问题吧,或者是不是有什么系统补丁没打

QQ373466062 发表于 2012-6-20 09:35:27

昨晚看了一下自己大二时候写的上位机,发现也是占用CPU20%多...    难道是MSCOMM这个Class底层封装不好?
回去看看源代码,再对比一下QT的第三方Serial库

skystalker 发表于 2012-6-20 11:44:55

QQ373466062 发表于 2012-6-20 09:35 static/image/common/back.gif
昨晚看了一下自己大二时候写的上位机,发现也是占用CPU20%多...    难道是MSCOMM这个Class底层封装不好?
...

QT的我测过 同样条件下CPU占用1-2% 不过具体是用什么控件不太清楚

skystalker 发表于 2012-6-20 11:46:09

gamethink 发表于 2012-6-11 12:08 static/image/common/back.gif
不会吧,我的程序运行后,打开串口什么都不干,CPU上升3%左右而已

要用下位机给上位机发数据测试CPU占用 5ms一包 一包30字节

bbs2009 发表于 2012-6-20 13:06:27

twitter 发表于 2012-6-20 14:53:37

本帖最后由 twitter 于 2012-6-20 14:57 编辑

问题在于,.NET做这种高频率硬件接口访问,效率是非常低的(相对于C++编译后的原生本地指令),数据得从驱动层复制到应用层,再从应用层转换到SerialPort类托管部分。CPU可能大部分都用来处理各层之间的转换,特别是后面的非托管到托管部分。
一般用于周期至少在100ms以上的处理,一次性读取并处理大量数据,而不是几个ms就要处理一小块数据。
比如这个老外就遇到类似的问题,http://www.hanselman.com/blog/PerformanceOfSystemIOPortsVersusUnmanagedSerialPortCode.aspx
最后还是得靠跳过SerialPort类,直接用API来处理。

skystalker 发表于 2012-6-20 21:59:11

twitter 发表于 2012-6-20 14:53 static/image/common/back.gif
问题在于,.NET做这种高频率硬件接口访问,效率是非常低的(相对于C++编译后的原生本地指令),数据得从驱 ...

嗯 我正在断断续续地把以前C写的WIN API串口移植到.NET下,有进展会及时贴在这里

skystalker 发表于 2012-6-20 22:03:26

bbs2009 发表于 2012-6-20 13:06 static/image/common/back.gif
5MS发50个字节,CPU占用率跳动不超过10%
-------------------------------------------------------------- ...

USB好是好 就是下位机移植起来太烦人了 上位机写驱动也烦,用HID的话又不够快

skystalker 发表于 2012-6-20 22:04:33

bbs2009 发表于 2012-6-20 13:06 static/image/common/back.gif
5MS发50个字节,CPU占用率跳动不超过10%
-------------------------------------------------------------- ...

嗯 可能是虚拟串口的原因 另外波特率是115200吗?

sunxinquan 发表于 2012-6-24 14:40:02

这个该怎么办

bbs2009 发表于 2012-6-24 14:58:38

skystalker 发表于 2012-6-24 21:06:37

bbs2009 发表于 2012-6-24 14:58 static/image/common/back.gif
回28 楼
USB好是好 就是下位机移植起来太烦人了 上位机写驱动也烦,用HID的话又不够快
------------------- ...

其实主要是兼容性问题 好多现成的东西都是串口的 除非自己做个转换器了

skystalker 发表于 2012-8-30 09:55:46

gamethink 发表于 2012-6-20 12:57 static/image/common/back.gif
我测试过了,5MS发50个字节,CPU占用率跳动不超过10%
不知道是不是用虚拟串口的原因 ...

我用USB串口测试了 不做任何操作 只接收数据 CPU占用是比用原生串口低。但如果要使用这些数据 CPU占用率会比用原生串口高很多

sys_suweixiao 发表于 2013-1-31 23:30:19

学习了C#串口通讯

sgzzour 发表于 2013-2-1 16:02:33

最近也在做c#串口部分,楼主的问题我倒真没注意。。。。。

yinnianlong 发表于 2013-4-21 15:42:23

我做了一下测试 1ms发送一次数据也才2%

yinnianlong 发表于 2013-4-21 15:42:51

LZ是不是每次接收都刷新UI

skystalker 发表于 2013-4-22 09:58:15

yinnianlong 发表于 2013-4-21 15:42 static/image/common/back.gif
LZ是不是每次接收都刷新UI

不刷新 不做任何处理

Shaw.Embedi 发表于 2013-10-8 10:10:06

时过一年多,楼主的问题解决了没有
我最近也是在用serialport,同样发现该问题
而且占用的CPU比你还高。
求帮助。

skystalker 发表于 2013-10-8 14:03:28

Shaw.Embedi 发表于 2013-10-8 10:10 static/image/common/back.gif
时过一年多,楼主的问题解决了没有
我最近也是在用serialport,同样发现该问题
而且占用的CPU比你还高。


我解决不了 而且不同的USB转串 性能差别很大

gallop020142 发表于 2013-10-9 16:28:09

Shaw.Embedi 发表于 2013-10-8 10:10 static/image/common/back.gif
时过一年多,楼主的问题解决了没有
我最近也是在用serialport,同样发现该问题
而且占用的CPU比你还高。


我前一段时间用api实现了串口,代码如下


      //===========================================================================//
      //                           串口API操作部分
      //===========================================================================//
      //win32 api constants
      private const uint GENERIC_READ= 0x80000000;
      private const uint GENERIC_WRITE = 0x40000000;
      private const intOPEN_EXISTING = 3;
      private const intINVALID_HANDLE_VALUE = -1;
      //
      // PURGE function flags.
      //
      private const int PURGE_TXABORT = 0x01;// Kill the pending/current writes to the comm port.
      private const int PURGE_RXABORT = 0x02;// Kill the pending/current reads to the comm port.
      private const int PURGE_TXCLEAR = 0x04;// Kill the transmit queue if there.
      private const int PURGE_RXCLEAR = 0x08;// Kill the typeahead buffer if there.

      private const int ERROR_IO_PENDING = 997;
      private const uint INFINITE = 0xFFFFFFFF;

      
      private struct DCB
      {
            //taken from c struct in platform sdk
            public int DCBlength;         // sizeof(DCB)
            public int BaudRate;            // current baud rate
            public int fBinary;          // binary mode, no EOF check
            public int fParity;          // enable parity checking
            public int fOutxCtsFlow;      // CTS output flow control
            public int fOutxDsrFlow;      // DSR output flow control
            public int fDtrControl;       // DTR flow control type
            public int fDsrSensitivity;   // DSR sensitivity
            public int fTXContinueOnXoff; // XOFF continues Tx
            public int fOutX;          // XON/XOFF out flow control
            public int fInX;         // XON/XOFF in flow control
            public int fErrorChar;   // enable error replacement
            public int fNull;          // enable null stripping
            public int fRtsControl;   // RTS flow control
            public int fAbortOnError;   // abort on error
            public int fDummy2;      // reserved
            public ushort wReserved;          // not currently used
            public ushort XonLim;             // transmit XON threshold
            public ushort XoffLim;            // transmit XOFF threshold
            public byte ByteSize;         // number of bits/byte, 4-8
            public byte Parity;             // 0-4=no,odd,even,mark,space
            public byte StopBits;         // 0,1,2 = 1, 1.5, 2
            public char XonChar;            // Tx and Rx XON character
            public char XoffChar;         // Tx and Rx XOFF character
            public char ErrorChar;          // error replacement character
            public char EofChar;            // end of input character
            public char EvtChar;            // received event character
            public ushort wReserved1;         // reserved; do not use
      }

      
      private struct COMMTIMEOUTS
      {
            public int ReadIntervalTimeout;
            public int ReadTotalTimeoutMultiplier;
            public int ReadTotalTimeoutConstant;
            public int WriteTotalTimeoutMultiplier;
            public int WriteTotalTimeoutConstant;
      }

      
      private struct OVERLAPPED
      {
            public int Internal;
            public int InternalHigh;
            public int Offset;
            public int OffsetHigh;
            public int hEvent;
      }
      
      
      private static extern int CreateFile(
          string lpFileName,                         // file name
          uint dwDesiredAccess,                      // access mode
          int dwShareMode,                        // share mode
          int lpSecurityAttributes, // SD
          int dwCreationDisposition,                // how to create
          int dwFlagsAndAttributes,               // file attributes
          int hTemplateFile                        // handle to template file
      );

      
      private static extern bool CloseHandle(
         int hObject                        // handle to object
         );

      
      private static extern bool ReadFile(
         int hFile,                     // handle to file
         byte[] lpBuffer,               // data buffer
         int nNumberOfBytesToRead,      // number of bytes to read
         ref int lpNumberOfBytesRead,   // number of bytes read
         ref OVERLAPPED lpOverlapped    // overlapped buffer
         );
      
      private static extern bool WriteFile(
         int hFile,                         // handle to file
         byte[] lpBuffer,                   // data buffer
         int nNumberOfBytesToWrite,         // number of bytes to write
         ref int lpNumberOfBytesWritten,    // number of bytes written
         ref OVERLAPPED lpOverlapped      // overlapped buffer
         );

      
      private static extern bool SetCommTimeouts(
         int hFile,                         // handle to comm device
         ref COMMTIMEOUTS lpCommTimeouts    // time-out values
         );
      
      private static extern bool GetCommTimeouts(
         int hFile,                         // handle to comm device
         ref COMMTIMEOUTS lpCommTimeouts    // time-out values
         );

      
      private static extern bool SetCommState(
         int hFile,             // handle to communications device
         ref DCB lpDCB          // device-control block
         );
      
      private static extern bool GetCommState(
         int hFile,             // handle to communications device
         ref DCB lpDCB          // device-control block
         );

      
      private static extern bool PurgeComm(int hFile, uint dwFlags);
      
      private static extern bool SetupComm(int hFile, int dwInQueue, int dwOutQueue);
      
      public static extern uint GetLastError();
      /*
      
      private static extern bool BuildCommDCB(
          string lpDef,// device-control string
          ref DCB lpDCB   // device-control block
      );


      
      
      public static extern bool EventModify(int hEvent, int dEvent);

      
      public static extern int CreateEventA(int lpEventAttributes, bool bManualReset, bool bIntialState, string lpName);
      
      public static extern bool ResetEvent(int hEvent);
      
      public static extern bool ClearCommError(int hFile, ref uint lpErrors, ref COMSTAT lpStat);

      
      private static extern int WaitForSingleObject(int hHandle, uint dwMilliseconds);
      
      private static extern bool GetOverlappedResult(int hFile, ref OVERLAPPED lpOverlapped, ref uint lpNumberOfBytesTransferred, bool bWait);
      
      public struct COMSTAT
      {
            public uint fCtsHold;
            public uint fDsrHold;
            public uint fRlsdHold;
            public uint fXoffHold;
            public uint fXoffSent;
            public uint fEof;
            public uint fTxim;
            public uint fReserved;
            public uint cbInQue;
            public uint cbOutQue;
      };

      public enum EventFlags
      {
            PULSE = 1,
            RESET = 2,
            SET = 3
      }
      */

      //private OVERLAPPED m_oSend = new OVERLAPPED();
      //private OVERLAPPED m_oRecv = new OVERLAPPED();

      /******************************************************************************
      * 函数名:MySerialOpen, MySerialClose
      * 输入:
      * 输出:
      * 功能:利用Windows API函数打开与关闭串口
      ******************************************************************************/
      private int hCom;
      private int ReadTimeout = 2000; //读超时
      private long m_dwSysErrCode = 0;
      private bool m_IsOpened = false;
      private bool MySerialOpen()
      {
            hCom = INVALID_HANDLE_VALUE;
            DCB dcbCommPort = new DCB();
            COMMTIMEOUTS ctoCommPort = new COMMTIMEOUTS();

            // OPEN THE COMM PORT.
            hCom = CreateFile(barEditItemUsart.EditValue.ToString(),
                GENERIC_READ | GENERIC_WRITE,
                0,
                0,
                OPEN_EXISTING,
                0,
                0);

            // IF THE PORT CANNOT BE OPENED, BAIL OUT.
            if (hCom == INVALID_HANDLE_VALUE)
            {
                MessageBox.Show("Com port can not be opened!");
                CloseHandle(hCom);
                return false;
            }

            PurgeComm(hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);

            if (SetupComm(hCom, m_MaxPackageSize, m_MaxPackageSize) == false)
            {
                m_dwSysErrCode = GetLastError();
                CloseHandle(hCom);
                return false;
            }

            dcbCommPort.DCBlength = Marshal.SizeOf(dcbCommPort);
            if (GetCommState(hCom, ref dcbCommPort) == false)
            {
                m_dwSysErrCode = GetLastError();
                CloseHandle(hCom);
                return false;
            }
            else
            {
                // SET BAUD RATE, PARITY, WORD SIZE, AND STOP BITS.
                // THERE ARE OTHER WAYS OF DOING SETTING THESE BUT THIS IS THE EASIEST.
                // IF YOU WANT TO LATER ADD CODE FOR OTHER BAUD RATES, REMEMBER
                // THAT THE ARGUMENT FOR BuildCommDCB MUST BE A POINTER TO A STRING.
                // ALSO NOTE THAT BuildCommDCB() DEFAULTS TO NO HANDSHAKING.               
                dcbCommPort.BaudRate = Convert.ToInt32(barEditItemBaud.EditValue.ToString());
                dcbCommPort.Parity = 0;//"0NONE 1Odd 2Even 3Mark 4Space"
                dcbCommPort.ByteSize = 8;
                dcbCommPort.StopBits = 1;//"0NONE 1One 2Two 3OnePointFive
                SetCommState(hCom, ref dcbCommPort);
            }

            GetCommTimeouts(hCom, ref ctoCommPort);
            ctoCommPort.ReadIntervalTimeout = 10;
            ctoCommPort.ReadTotalTimeoutMultiplier = 1;
            ctoCommPort.ReadTotalTimeoutConstant = 1;
            ctoCommPort.WriteTotalTimeoutMultiplier = 1000;
            ctoCommPort.WriteTotalTimeoutConstant = 500000;
            SetCommTimeouts(hCom, ref ctoCommPort);

            //m_oSend.hEvent = CreateEventA(null, false, false, null);
            //m_oRecv.hEvent = CreateEventA(null, false, false, null);

            m_IsOpened = true;
            return true;
      }
      private bool MySerialClose()
      {
            if (hCom != INVALID_HANDLE_VALUE)
            {
                CloseHandle(hCom);
                Thread.Sleep(60);
                hCom = INVALID_HANDLE_VALUE;
            }
            m_IsOpened = false;
            return true;
      }

      
      
      
      private void ComReceiveThreadFunc()
      {
            byte[] buf = new byte;
            int nr = 0;
            uint dwRead = 0;
            bool bResult = false;
            while (m_RecvThreadStopFlag != true)
            {               
                byte[] BufBytes = new byte;
                if (hCom != INVALID_HANDLE_VALUE)
                {
                  OVERLAPPED ovlCommPort = new OVERLAPPED();
                  int BytesRead = 0;
                  ReadFile(hCom, BufBytes, 1024, ref BytesRead, ref ovlCommPort);
                  if (BytesRead != 0)
                  {
                        lock (myLock)
                        {
                            for (int i = 0; i < BytesRead; i++)
                            {
                              receiveBuffQueue.Enqueue(BufBytes);
                              receiveBuffSemaphore.Release(1);
                              receiveBuffShowQueue.Enqueue(BufBytes);
                              m_totalByte++;
                            }
                        }
                  }
                }
            }
      }
      
      
      public int ComWriteBuf(byte[] byteBuf, int dwLen)
      {
            if (byteBuf.Length == 0)
                return 0;
            
            if (hCom != INVALID_HANDLE_VALUE)
            {
                OVERLAPPED ovlCommPort = new OVERLAPPED();
                int bytesWritten = 0;
                WriteFile(hCom, byteBuf, dwLen, ref bytesWritten, ref ovlCommPort);
                return bytesWritten;
            }
            else
            {
                return -1;
            }
      }

具体的效率我也没有来得及测试,仅供参考。

gongxd 发表于 2014-1-27 20:52:44

我最近也是在用serialport,同样发现该问题,这个问题在电脑接收数据的时候比较明显,在接受事件里面什么也不做 只读取数据占用cpu都不低

通过在数据接收事件里面 加thread.sleep(50) 可以降低一点,其实就是让数据现在缓冲到FIFo里面,50ms后再处理

可以自己反编译 serialport的源代码,里面的功能很多 很复杂

串口处理数据 最好用 c#的 线程安全队列在多线程里面很好用 线程处理数据放队列里外部读取数据时再从队列读取

error_dan 发表于 2014-1-27 22:00:10

gongxd 发表于 2014-1-27 20:52
我最近也是在用serialport,同样发现该问题,这个问题在电脑接收数据的时候比较明显,在接受事件里面什么也 ...

通过设置串口的数据接收事件触发阈值可以有针对性的降低CPU占用率,简单来说可以视为减少进入中断的次数,serialport有两种读取方式,一个是ReadByte每次读取一个字节,一个是Read可以批量读取数据,后面一种明显效率高多了.
其实最极端的方法可以根本不用数据接收事件,跑一个后台线程查询串口的buffer数量然后批量处理,PC机上有N大的缓存不用太浪费了.

gongxd 发表于 2014-1-28 07:04:11

也设置 触发阈值了,但是感觉设高了反应迟钝
页: [1]
查看完整版本: C#串口通信CPU占用问题