2

I am using Win32 API (Overlapped IO) for serial port communication. I have used PInvoke to call the Win32 API from my C# code.

In my previous implementation, the receiver thread reads data by using Polling mechanism (Infinite while loop reads RX buffer periodically). Now, I am trying to use Event-Driven approach to read the data (Using WaitCommEvent API Call). We expect the new implementation will reduce the CPU usage and Speed-Up the communication.

But, After changing to Event-Driven approach, the communication is actually slowed down, taking twice the time to read when compared to Polling mechanism.

Can anyone suggest which one of these is the best approach improve the communication speed and reduce the CPU usage?

Below is my code snippet:

private void ReadDataForWindows()
{
    var data = new byte[255];

    // Specify a set of events to be monitored for the port.
    if (SetCommMask(this.Handle, EV_DEFAULT))
    {
        while (this.Handle != (IntPtr)INVALID_HANDLE_VALUE)
        {
            uint eventMask = 0;
            // Wait for an event to occur for the port.
            eventMask = this.WaitForEvent();

            if (eventMask == EV_RXCHAR)
            {
                this.ReadFromRXBuffer(data);
            }
            else if (eventMask == EV_ERR)
            {
                uint errors;
                ClearCommError(this.Handle, out errors, IntPtr.Zero);
            }

            // Re-specify the set of events to be monitored for the port.
            if (!SetCommMask(this.Handle, EV_DEFAULT))
            {
                break;
            }
        }
    }
}

private void ReadFromRXBuffer(byte[] data)
{
    uint dwBytesTransferred;
    // Loop for waiting for the data.
    do
    {
        // Read the data from the serial port.
        this.Read(data, 255, out dwBytesTransferred);

        // Display the data read.
        if (dwBytesTransferred > 0)
        {
            if (this.DataReceived != null)
            {
                var dataReceived = new byte[dwBytesTransferred];
                for (var i = 0; i < dwBytesTransferred; i++)
                {
                    dataReceived[i] = data[i];
                }

                this.DataReceived(this, new ByteDataReceivedEventArgs(dataReceived, Convert.ToInt32(dwBytesTransferred)));
            }
        }

    } while (dwBytesTransferred > 0);
}

private uint WaitForEvent()
{
    uint eventMask = 0;
    IntPtr evPtr = Marshal.AllocHGlobal(Marshal.SizeOf(eventMask));
    Marshal.WriteInt32(evPtr, 0);

    if (WaitCommEvent(this.Handle, evPtr, this.RxOvr.MemPtr) == false)
    {
        int error = Marshal.GetLastWin32Error();

        // Operation is executing in the background
        if (error == ERROR_IO_PENDING)
        {
            this.ReceiveSignal.WaitOne();
        }
        else
        {
            this.Fault = "WaitCommEvent() Failed. System Returned Error Code: " +
                error.ToString();
            return 0;
        }
    }

    eventMask = (uint)Marshal.ReadInt32(evPtr);
    return eventMask;
}
petchirajan
  • 4,152
  • 1
  • 18
  • 20
  • You are just not ahead with this code. You block on the WaitOne() call instead of the ReadFile() call. The point of overlapped I/O is that you can do something else while the I/O request is pending. That "something else" tends to be hard to come by, you must have other operations in your code that block so you can use WaitForMultipleObjects(). Using an I/O completion port is the better mouse trap, supported for WaitCommEvent(). RegisterWaitForSingleObject() is easy to get going but less efficient. – Hans Passant Jun 23 '14 at 10:23
  • Win32 Serial API through PINvoke? Why don't you use SerialPort class? – Alex F Jun 23 '14 at 10:36
  • Thanks for your response Hans. ReadDataForWindows is running in separate thread. This thread notifies the UI when there is any data received from the device. Are you suggesting me to use WaitForMultipleObjects instead of WaitCommEvent? Is there any performance differences between WaitForMultipleObjects and WaitCommEvent? – petchirajan Jun 23 '14 at 10:39
  • @Alex - C# Seriapl port class has performance problems. It will take more than 30% CPU usage. But Win32 API will take only 6 to 15% CPU usage. So that only I preferred Win32 API. – petchirajan Jun 23 '14 at 10:41
  • Working with Win32 API you should wait for overlapped event using WaitForSingleObject. This should be done after ReadFile call, which returns immediately nn overlapped mode. If you want to cancel reading thread, you add one more event used to stop waiting, and replace WaitForSingleObject withWaitForMultipleObjects. All this stuff is done in the SerialPort class, providing asynchronous DataReceived event. CPU usage on wait operation must be 0 in both cases. – Alex F Jun 23 '14 at 11:58

0 Answers0