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);

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


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

与前版功能基本相似,修正了部分bug,对界面进行了美化,目前这个美化相信应该够用了,因为时间问题没有增加可自定义界面功能,等有时间再提供吧,压缩文件使用“好压”做的,里面有一个例子,图片资源编辑工具,大家可以测试一下效果,里面也有详细说明,图片资源编辑器大家也可以用到自己的程序中,方便程序的图片统一管理和调用,菜鸟,达人们别笑话我了。 这里还是在说一下等待窗体的具体使用方法吧 首先将LOADing.dll,DevComponents.DotNetBar2.dll两个dll文件复制到你程序目录中,在程序项目中引用LOADing.dll,在要使用的地方 //先实例 LOADing.FORMshow FRload = new LOADing.FORMshow(); //再调用showto方法,其中的参数this为你调用等待窗体的主窗体对象,delegate { }为委托,IMGclass_AddFlie_r()为功能处理函数,其中所传递的参数第一的FRload必须为固定的创建等待窗体的实例对象,后面跟所需要传递的对象参数。 FRload.showto(this, delegate { IMGclass_AddFlie_r(new object[] { FRload, iclass, fileDialog1.FileNames, _at.SelectedNode.Text }); },true); //这个为数据处理部分 private void IMGclass_AddFlie_r(object[] d) { for (int i = 0; i <= ((string[])d[2]).Length - 1; i++) { ((IMGclass)d[1]).top[d[3].ToString()].Add("标" + ((IMGclass)d[1]).top[d[3].ToString()].Count, BinToCmd(((string[])d[2])[i])); f_new_hand(new object[] { ((IMGclass)d[1]).top[d[3].ToString()], "标" + (((IMGclass)d[1]).top[d[3].ToString()].Count - 1) }); ((LOADing.FORMshow)d[0]).send("加载图片文件:", Convert.ToInt32((Convert.ToSingle(i) / (Convert.ToSingle(((string[])d[2]).Length) / Convert.ToSingle(100))))); } BinToclass(((IMGclass)d[1]), _path[_at.SelectedNode.Parent.Text]); } 好了,使用起来很简单,看看上面的例子就了,如需索要源码或者要提问的话,请联系QQ76230454.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值