在写程序中,会经常遇到与时间有关系的代码,比如延迟、周期性调用某函数、获取系统时间等等,如果时间定的不准确,可以想象该系统的不可靠性。在LabwindowsCVI中,有一种简单的定时器实现方式,可直接添加Timer定时器控件,然后设置Timer的调度周期和回调函数,这样主线程就会在周期内调用Timer的回调函数了,但这种方式的实现精度并不高,很容易受其它应用程序及主线程其它应用代码的阻塞。本章将介绍一种高精确定时方式的实现,其简单来说就是开一个优先级较高的线程,然后该线程查询系统的晶振时间,通过晶振时间来实现精确定时。下面将介绍LabwindowsCVI对线程的创建及系统晶振时间的读取。
线程:
LabwindowsCVI对线程支持的API如下所示:
CmtNewThreadPool:
CmtNewThreadPool用于创建一个线程池,该函数原型如下所示:
int CmtNewThreadPool (int maximumNumberOfThreads, CmtThreadPoolHandle *poolHandle);
其中maximumNumberOfThreads为该线程池线程的数量,poolHandle为创建成功后的线程池句柄。
CmtScheduleThreadPoolFunctionAdv:
CmtScheduleThreadPoolFunctionAdv用于设置一个线程来执行任务函数,其函数原型如下所示:
int CmtScheduleThreadPoolFunctionAdv (CmtThreadPoolHandle poolHandle, CmtThreadFunctionPtr threadFunction, void *threadFunctionData, int threadFunctionPriority, CmtThreadFunctionCallbackPtr callbackFunction, unsigned int callbackEventMask, void *callbackData, unsigned int callbackThreadID, CmtThreadFunctionID *threadFunctionID);
其中poolHandle为CmtNewThreadPool创建的线程池句柄,threadFunction为线程函数即任务函数,threadFunctionData为线程函数的参数,threadFunctionPriority为调用线程函数线程的优先级,callbackFunction为线程回调函数,该函数在任务函数执行前后运行,callbackEventMask为线程回调函数事件的位屏蔽,callbackData为线程回调函数的参数,callbackThreadID为执行线程函数的线程ID,threadFunctionID为线程函数的ID。
CmtWaitForThreadPoolFunctionCompletion:
CmtWaitForThreadPoolFunctionCompletion为等待线程函数的执行完成,其函数原型如下所示:
int CmtWaitForThreadPoolFunctionCompletion (CmtThreadPoolHandle poolHandle, CmtThreadFunctionID threadFunctionID, unsigned int options);
其中poolHandle为线程池的句柄,threadFunctionID为线程函数的ID,options为等待该线程函数执行完时是否处理事件。
线程函数:
线程函数也是任务函数,其函数的原型如下所示:
int CVICALLBACK ThreadFunction (void *functionData);
通常我们在线程函数中会执行一个死循环直到应用程序退出,否则该线程将占用系统资源而不释放,导致电脑系统卡顿。
晶振时间:
LabwindowsCVI利用windows库里面的API来实现晶振时间的读取,其API如下所示:
QueryPerformanceCounter:
QueryPerformanceCounter用于查询系统晶振计数,其函数原型如下所示:
WINBASEAPI BOOL WINAPI QueryPerformanceCounter(__out LARGE_INTEGER *lpPerformanceCount);
其中lpPerformanceCount为查询的晶振计数。
QueryPerformanceFrequency:
QueryPerformanceFrequency用于查询晶振每秒的计数,其函数原型如下所示:
WINBASEAPI BOOL WINAPI QueryPerformanceFrequency(
__out LARGE_INTEGER *lpFrequency);
其中lpFrequency为查询的晶振每秒的计数。
项目实践:
新建LabwindowsCVI工程,在c文件头文件声明最开始的地方添加#include <windows.h>声明,否则将与头文件utility.h和formatio.h命名冲突,导致编译通不过。工程代码如下所示:
#include <windows.h>
#include <ansi_c.h>
#include <utility.h>
#include <userint.h>
#include <cvirte.h>
#include "HighPrecisionTimerTest.h"
static int panelHandle;
CmtThreadFunctionID backgroundThreadFunctionID;
volatile int AppRunningFlag = 0;
CmtThreadPoolHandle threadPoolHandle;
char errorStringBuffer[256];
int CVICALLBACK BackgroundThreadFunction (void *functionData);
int CreateBackgroundThread(void)
{
int ret = -1;
ret = CmtNewThreadPool (1, &threadPoolHandle);
if(ret < 0)
{
CmtGetErrorMessage(ret,errorStringBuffer);
printf("%s\r\n",errorStringBuffer);
}
else
{
ret = CmtScheduleThreadPoolFunctionAdv(threadPoolHandle,BackgroundThreadFunction,NULL,THREAD_PRIORITY_HIGHEST,NULL,EVENT_TP_THREAD_FUNCTION_END,NULL,RUN_IN_SCHEDULED_THREAD,&backgroundThreadFunctionID);
if(ret < 0)
{
CmtGetErrorMessage(ret,errorStringBuffer);
printf("%s\r\n",errorStringBuffer);
}
}
return ret;
}
int QuitBackgroundThread(void)
{
CmtWaitForThreadPoolFunctionCompletion(threadPoolHandle,backgroundThreadFunctionID,OPT_TP_PROCESS_EVENTS_WHILE_WAITING);
return 0;
}
int QueryTickPeriod(LARGE_INTEGER *begin,LARGE_INTEGER *end,LONGLONG tick)
{
if(end->QuadPart >= begin->QuadPart)
{
if((end->QuadPart - begin->QuadPart) >= tick)
{
return 1;
}
else
{
return 0;
}
}
else
{
if((0xFFFFFFFFFFFFFFFF - begin->QuadPart + end->QuadPart) >= tick)
{
return 1;
}
else
{
return 0;
}
}
}
void Task1ms(void)
{
static int cnt =0;
if(cnt >= 1000)
{
printf("1s count\r\n");
cnt = 0;
}
else
{
cnt ++;
}
}
int CVICALLBACK BackgroundThreadFunction (void *functionData)
{
LARGE_INTEGER begin, end, ticksPerSecond;
LONGLONG tickPeriod;
AppRunningFlag = 1;
QueryPerformanceFrequency (&ticksPerSecond);
tickPeriod = ticksPerSecond.QuadPart/1000;//可修改tick的周期值
while(AppRunningFlag)
{
QueryPerformanceCounter (&begin);
QueryPerformanceCounter (&end);
while(QueryTickPeriod(&begin,&end,tickPeriod) == 0)
{
QueryPerformanceCounter (&end);
}
Task1ms();
}
return 0;
}
int main (int argc, char *argv[])
{
if (InitCVIRTE (0, argv, 0) == 0)
return -1; /* out of memory */
if ((panelHandle = LoadPanel (0, "HighPrecisionTimerTest.uir", PANEL)) < 0)
return -1;
DisplayPanel (panelHandle);
CreateBackgroundThread();
RunUserInterface ();
QuitBackgroundThread();
DiscardPanel (panelHandle);
return 0;
}
int CVICALLBACK quit (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
switch (event)
{
case EVENT_COMMIT:
QuitUserInterface(0);
AppRunningFlag = 0;
break;
}
return 0;
}
实践效果:
后台线程每秒钟打印 1s count,如下图所示:
后话:
本测试工程实现了高精度的计时,用户可以将BackgroundThreadFunction当成工作线程,添加自己的其它任务代码。