在工业软件、上位机等需要对硬件设备进行实时通信的开发过程中,我们经常会使用死循环来循环读取缓冲区的数据。如下所示
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一般很高)。为了防止这个现象,我们一般会在程序中加一个等待,如下。
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)
自旋
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);
}
}
同步信号等待
有时我们也可以让线程等待一个同步信号,如下所示
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);
这样的话,还可以同步监听系统是否退出。同步准确关闭线程。
积跬步以至千里:) (:一阵没来由的风