C# 死循环等待方式

C# 死循环等待方式

在工业软件、上位机等需要对硬件设备进行实时通信的开发过程中,我们经常会使用死循环来循环读取缓冲区的数据。如下所示

  private void Receive()
        {
            while (true)
            {
                //返回接收缓冲区中尚未被读取的帧数
                UInt32 num = VCI_GetReceiveNum(devType, devIndex, devChannel);
                if (num == 0)
                {
                    continue;
                }
                //分配一次最多接收VCI_GetReceiveNum函数返回值的帧数的数据存储内存空间
                IntPtr pt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(VCI_CAN_OBJ)) * (int)num);
                //返回实际读到的帧数,如返回的为0xFFFFFFFF,则读取数据失败,有错误发生。
                UInt32 len = VCI_Receive(devType, devIndex, devChannel, pt, num, -1);
                if (len == 0xFFFFFFFF)
                {
                    VCI_ReadErrInfo(devType, devIndex, devChannel, ref pErrInfo);
                    //释放分配的内存空间
                    Marshal.FreeHGlobal(pt);
                    continue;
                }

                //获取CAN总线上的数据并触发事件
                for (int i = 0; i < len; i++)
                {
                    VCI_CAN_OBJ receData = (VCI_CAN_OBJ)Marshal.PtrToStructure((IntPtr)((UInt32)pt + i * Marshal.SizeOf(typeof(VCI_CAN_OBJ))), typeof(VCI_CAN_OBJ));

                    DataReceived?.Invoke(receData);
                }
                Marshal.FreeHGlobal(pt);
            }
        }

当设备中无数据,我们却继续取读取串口数据时,这样会让线程不间隙的做无用功(此时CPU一般很高)。为了防止这个现象,我们一般会在程序中加一个等待,如下。

Thread.Sleep()

  private void Receive()
        {
            while (true)
            {
                //返回接收缓冲区中尚未被读取的帧数
                UInt32 num = VCI_GetReceiveNum(devType, devIndex, devChannel);
                if (num == 0)
                {
                    continue;
                }
                //分配一次最多接收VCI_GetReceiveNum函数返回值的帧数的数据存储内存空间
                IntPtr pt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(VCI_CAN_OBJ)) * (int)num);
                //返回实际读到的帧数,如返回的为0xFFFFFFFF,则读取数据失败,有错误发生。
                UInt32 len = VCI_Receive(devType, devIndex, devChannel, pt, num, -1);
                if (len == 0xFFFFFFFF)
                {
                    VCI_ReadErrInfo(devType, devIndex, devChannel, ref pErrInfo);
                    //释放分配的内存空间
                    Marshal.FreeHGlobal(pt);
                    continue;
                }

                //获取CAN总线上的数据并触发事件
                for (int i = 0; i < len; i++)
                {
                    VCI_CAN_OBJ receData = (VCI_CAN_OBJ)Marshal.PtrToStructure((IntPtr)((UInt32)pt + i * Marshal.SizeOf(typeof(VCI_CAN_OBJ))), typeof(VCI_CAN_OBJ));

                    DataReceived?.Invoke(receData);
                }
                Marshal.FreeHGlobal(pt);
                
                // 让线程等待休息
                Thread.Sleep(1);
            }
        }

除了这种方式,还有其他的线程等待方式。比如自旋(SpinWait.SpinUntil(() => false, 1);)同步信号(ManualResetEvent)

SpinWait.SpinUntil

自旋

  private void Receive()
        {
            while (true)
            {
                //返回接收缓冲区中尚未被读取的帧数
                UInt32 num = VCI_GetReceiveNum(devType, devIndex, devChannel);
                if (num == 0)
                {
                    continue;
                }
                //分配一次最多接收VCI_GetReceiveNum函数返回值的帧数的数据存储内存空间
                IntPtr pt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(VCI_CAN_OBJ)) * (int)num);
                //返回实际读到的帧数,如返回的为0xFFFFFFFF,则读取数据失败,有错误发生。
                UInt32 len = VCI_Receive(devType, devIndex, devChannel, pt, num, -1);
                if (len == 0xFFFFFFFF)
                {
                    VCI_ReadErrInfo(devType, devIndex, devChannel, ref pErrInfo);
                    //释放分配的内存空间
                    Marshal.FreeHGlobal(pt);
                    continue;
                }

                //获取CAN总线上的数据并触发事件
                for (int i = 0; i < len; i++)
                {
                    VCI_CAN_OBJ receData = (VCI_CAN_OBJ)Marshal.PtrToStructure((IntPtr)((UInt32)pt + i * Marshal.SizeOf(typeof(VCI_CAN_OBJ))), typeof(VCI_CAN_OBJ));

                    DataReceived?.Invoke(receData);
                }
                Marshal.FreeHGlobal(pt);
                
                // 让线程等待休息
                SpinWait.SpinUntil(() => false, 1);
            }
        }

ManualResetEvent.WaitOne()

同步信号等待

有时我们也可以让线程等待一个同步信号,如下所示

 private void Receive()
        {
            while (true)
            {
                //返回接收缓冲区中尚未被读取的帧数
                UInt32 num = VCI_GetReceiveNum(devType, devIndex, devChannel);
                if (num == 0)
                {
                    continue;
                }
                //分配一次最多接收VCI_GetReceiveNum函数返回值的帧数的数据存储内存空间
                IntPtr pt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(VCI_CAN_OBJ)) * (int)num);
                //返回实际读到的帧数,如返回的为0xFFFFFFFF,则读取数据失败,有错误发生。
                UInt32 len = VCI_Receive(devType, devIndex, devChannel, pt, num, -1);
                if (len == 0xFFFFFFFF)
                {
                    VCI_ReadErrInfo(devType, devIndex, devChannel, ref pErrInfo);
                    //释放分配的内存空间
                    Marshal.FreeHGlobal(pt);
                    continue;
                }

                //获取CAN总线上的数据并触发事件
                for (int i = 0; i < len; i++)
                {
                    VCI_CAN_OBJ receData = (VCI_CAN_OBJ)Marshal.PtrToStructure((IntPtr)((UInt32)pt + i * Marshal.SizeOf(typeof(VCI_CAN_OBJ))), typeof(VCI_CAN_OBJ));

                    DataReceived?.Invoke(receData);
                }
                Marshal.FreeHGlobal(pt);
                
                // 让线程等待休息
                if (shutDownEvent.WaitOne(TimeSpan.FromMilliseconds(1)))
                {
                    Console.WriteLine("采集县城推出");
                    break;
                }
            }
        }
        
        // 系统退出同步信号
   readonly ManualResetEvent shutDownEvent = new ManualResetEvent(false);

这样的话,还可以同步监听系统是否退出。同步准确关闭线程。


积跬步以至千里:) (:一阵没来由的风

你可能感兴趣的:(C#,.Net,c#,多线程,死循环,有效等待,同步信号)