LabWindows/CVI多线程技术的应用
2015-12-20杨秋虎
杨秋虎
(昆明船舶设备试验研究中心第5研究室,云南昆明 650051)
LabWindows/CVI是美国国家仪器公司推出的基于ANSI C的虚拟仪器开发平台,包含了集成式开发环境、交互式编程方法、简单直观的图形用户界面设计、完善的兼容性、灵活的调试手段以及功能强大的函数库,适用于测试测量、自动控制、数据通信、信号分析等领域[1-3]。
操作系统中,定义应用程序的一次动态执行为进程,而线程是进程内部程序执行的路径,是进程的一个执行单元。即线程是可由系统调度的一个最简单的代码单元,负责执行包含在进程的地址空间中的程序代码[4-6]。
在构建大型系统或复杂多任务系统中,多任务并行执行所带来的系统开销以及任务之间的耦合问题尤为重要,处理不好就会导致系统崩溃。在注重系统效率与性能之间的平衡时,恰当地使用多线程,使得各个任务之间可以在互不干扰的情况下顺利运行,可以大幅提高系统实时响应特性。
应用多线程应用程序的优势在于充分利用了CPU的空闲时间片,可用较短的时间来响应用户的要求,使得进程整体运行效率得到较大提高。同时,同一线程下的多个线程共享同一片内存,所以无需要额外构建数据传送机制,数据共享方便[7]。
1 LabWindows/CVI多线程技术
LabWindows/CVI提供了完善的多线程库来实现多线程编程,与Windows提供的软件开发工具包Windows SDK threading API相比,其进行了以下优化:(1)利用线程池技术完成线程管理,将函数调度到独立的线程中执行。(2)利用线程安全队列完成线程之间的数据传递,保证线程可在另一个线程向队列中写数据的同时读取队列的数据。(3)提供线程锁机制完成全局变量的互斥使用。(4)提供了线程安全变量的数据保护方式。(5)提供了精度较高的异步定时器。
LabWindows/CVI是在辅助线程中运行代码,主线程从main函数开始执行,在主线程特定位置开始辅助线程的执行。典型的应用界面中,主线程完成的主要任务包括创建、显示和运行控制界面,而利用辅助线程完成实时性较高的任务或操作,如实时通信,数据采集等。LabWindows/CVI提供了两种在辅助线程中运行代码的高级机制,分别是异步定时器和线程池技术。线程池适用于运行若干次执行或不连续执行的任务,而异步定时器则适用于定期执行的任务。
1.1 异步定时器
LabWindows/CVI的toolslib库中提供了一系列的异步定时器访问与操作函数,异步定时器不同于面板上的常规定时器控件,有不同的调用方式,只能通过程序代码中调用定时器新建函数NewAsyncTimer(void)实现。在主线程运行时,为异步定时器运行分配一个辅助线程。需要注意的是,如果使用多个异步定时器,其参数可能被其他线程所修改,导致程序运行产生不必要的结果,所以不建议使用多个异步定时器。其次,异步定时器本质上使用的Windows多媒体定时器来实现定时功能,多媒体定时器的最小分辨率在不同的电脑上可能不同,若设定值小于最小分辨率,程序运行会出现不确定的结果,故而,推荐使用的分辨率不 <10 ms[8-10],若定时时间<10 ms,则需采用更高精度的硬件定时器。
通过Suspend Async Timer Callbacks(void)函数与Resume Async Timer Callbacks(void)函数实现所有异步定时器的挂起与恢复操作。通过设置定时器属性函数可设置特定定时器的定时时间、启动停止以及优先级。异步定时器使用完毕后,应及时释放异步定时器以释放占用的系统资源。
1.2 线程池技术
使用线程池技术,若不使用系统的默认线程池,需要在主线程调用函数CmtNewThreadPool创建新的线程池,获取线程池句柄并设定线程池可执行的最大线程数,分配线程时有两种分配方式,一种是不考虑优先级直接分配,适合于只有单个辅助线程或线程之间不存在耦合或冲突的程序,通过调用函数CmtScheduleThread-PoolFunction实现;对于多个线程并行执行的程序,根据任务要求划分线程优先级,确保时间要求严格的线程及时执行,调用函数 CmtScheduleThreadPoolFunctionAdv在分配线程的同时确定线程的优先级,并确定线程执行起始和结束是否需要添加回调函数,回调函数函数的有用之处在于可以在线程结束之后刷新主界面的相关信息,而无需在新建线程完成界面刷新,避免额外的系统开销。线程的优先级划分为7个级别,一般情况下,要确保线程的优先级别不高于系统响应界面操作的优先级。在线程执行完毕后,需要及时地释放线程。
2 数据保护
多个线程并行执行时,数据保护问题尤为关键。线程之间存在耦合时,多个线程可能都对某一变量进行访问,在线程执行过程中,变量值的改变可能会影响其它线程的执行,出现不可预料的后果。需要保护的变量特点是被两个及以上线程访问,在调试过程中,如果不注意数据保护问题,可能不会导致发生致命性的错误,但在发布版的情况下就会出现很多问题,所以从程序编写之初就应该将数据保护纳入考虑。一般情况下,需要保护的数据有全局变量、静态局部变量以及动态分配的变量和内存。LabWindows/CVI提供了3种数据保护机制:线程锁、线程安全变量与线程安全队列。
2.1 线程锁
线程锁将需要保护的对象与线程锁结合起来,需要保护的对象可以是某个变量,某一段代码或是第3方库函数。在每次访问这些对象之前,必须调用CmtNewLock获取线程锁获取才能运行代码或访问变量,执行完毕后调用CmtDiscardLock立即释放线程锁。若某线程访问变量时线程锁正被其他线程占用,则该线程需等待其他线程释放线程锁之后才能访问该变量。这种方法适用于需要保护变量不多的情况,当有多个线程锁存在时,要避免线程之间互相占有对方正在等待的线程锁。这会导致程序死锁、界面卡死。
2.2 线程安全变量
线程安全变量实际上结合了线程锁的特点,在函数宏定义中利用 DefineThreadSafeVar(datatype,Varname)创建线程安全变量,并且只能通过如下与之匹配的线程安全变量操作函数对其进行特定访问(VarName代表实际变量名,datatype代表变量的具体类型,可以是基本类型变量,也可以是数组,结构体等):
int InitializeVarName(void);//初始化
void UninitializeVarName(void);//卸载
datatype*GetPointerToVarName(void);//获取指针
void ReleasePointerToVarName(void);//释放指针
void SetVarName(datatype val);//设置变量值
datatype GetVarName(void);//获取变量值
线程安全变量程序运行之前必须调用初始化函数进行初始化,运行结束后要及时进行卸载。访问或设置变量可以采用指针或调用相关函数的方式完成,调用指针完成变量访问之后需要及时释放指针。
2.3 线程安全队列
线程安全队列,可在线程之间快速安全地传输数据,特别适用于一个线程不断向队列写数据,另一个线程不断读取的情况,可避免对数据同时读写发生冲突,例如数据采集、实时通信等高速读写的任务。通常辅助线程负责读取数据或获取实时信息,主线程读取数据进行分析并显示。
3 多线程在虚拟仪器控制软件中应用
在编写某仪器自动控制软件过程中,充分利用了多线程技术。按照软件需求,将需要处理的任务划分为界面响应、实时通信、数据显示及数据分析。所有数据都是通过RS485通信方式,由网络内各从站通过总线发送到上位机。根据具体的任务要求将各任务分配到各线程中执行,将界面响应作为主线程,实时通信、数据显示和数据分析作为辅助线程,在并发的辅助线程中,考虑到实时通信对系统的重要性,将实时通信线程的优先级设为最高,仅次于主线程,由于存在通讯数据的写入与读取会同时发生的情况,所以对于通讯数据采用线程安全队列技术,避免读写同时进行时发生冲突,各线程具体实现方法可以是线程池技术,也可是异步定时器。
3.1 界面响应线程
界面具有自动控制系统的基本参数设置信息、操作按钮、实时运行状态显示等功能。界面响应线程作为主线程,需及时响应各种消息,并对操作人员的操作出快速响应。
3.2 实时通信线程
该线程在系统开始运行后自动运行,负责与3个从站不断进行通信,获取从站的运行信息,由于采用请求/应答通讯方式,各从站只在主站发出请求信息时回复主站的查询或控制信息,故而采用轮询的方式对各子站进行查询。
实时通信线程中的通信信息既包含从站对主站的响应信息,也包含主站对从站的控制信息。响应信息中包含了各子站的运行状态信息,这些信息存在同时被数据分析线程和实时通信线程访问的可能性,这些数据需要考虑数据保护。考虑到通讯的数据量较大,故新建一个线程安全队列,实时通信线程每次接收到合法信息之后,都写入线程安全队列中,写入完毕之后触发线程安全队列回调函数完成数据分析。通信线程负责写数据进线程安全队列,数据分析线程用于从线程安全队列中读取数据,则两个线程之间不会因同时访问数据发生冲突。
3.3 数据显示线程
对接收到的数据通常需要进行及时显示,这些数据代表了系统的实时运行状态和各从站的运行信息,实现的方法有两种,可与通信线程类似采用线程池技术,读取线程安全队列内的数据,作相应的分析处理后在界面上进行实时显示。也可利用异步定时器定时刷新界面上显示控件的信息。
3.4 数据分析线程
数据分析线程对接收到的每个子站的运行信息进行分析处理,如信号处理和曲线拟合等,采用线程池技术新建数据分析线程实现。
系统运行过程中,由于主线程处于最高优先级,所以当主线程响应用户界面消息时,会导致实时通信线程、数据显示线程以及数据分析线程暂时挂起,优先响应界面消息,界面响应执行完毕后,继续执行挂起的线程。这种处理方法的好处在于,可以并发地执行多个任务且不发生冲突,最大效率地利用了系统资源。
4 试验结果
系统运行的界面如图1所示,通过控制按钮可完成从站的操作,如阀门开启或关闭,电机速度设置与启停控制等,界面上的显示框实时显示从站的运行信息。试验验证采用同样的界面进行,分别不采用多线程技术和采用多线程技术两种方式来实现界面功能。由于系统运行时并无直观数据反映两种方式的差别,所以对试验结果的描述只进行定性描述。经过多次试验验证,采用多线程技术时各组件的控制与界面实时操作均能及时完成,当用户界面显示信息不断刷新时,通过点击界面上的控制按钮,可以较好地控制各子站的动作状态,并未发生冲突或是不响应、界面卡死的现象。而未采用多线程技术的方式,多个任务之间不能协调进行,系统长时间处于响应系统通信任务或界面刷新任务的状态,对界面操作响应延迟或基本不响应或是出现卡死现象,严重制约了系统的实时性要求。
图1 系统运行界面
5 结束语
在单任务系统中,多线程技术的优点不明显,而在多任务并行的系统中,多线程技术具有较大优势,对于单核系统,通过将线程分配到离散的时间片上执行,对于多核系统,将线程分配给不同的CPU执行,可以最大限度地利用系统资源,完成并行任务的执行而不发生阻塞。LabWindows/CVI作为虚拟仪器软件,首先在界面开发上大幅缩短了时间,采用多线程技术后,使其在自动控制领域的优点得以凸显。
[1]王建新.LabWindows/虚拟仪器高级应用[M].北京:化学工业出版社,2013.
[2]王建新,隋美丽.LabWindows/CVI虚拟仪器测试技术及工程应用[M].北京:化学工业出版社,2011.
[3]National Instrument Corp.LabWindows/CVI programmer reference manual [M].Dex USA:National Instrument Corp,2001.
[4]杨东升,王高峰.多线程技术在虚拟仪器开发软件Lab-Windows/CVI的实现[J].电测与仪表,2005(3):39-41.
[5]李敏智.基于LabWindows/CVI的数据采集与监控系统的设计与实现[D].武汉:武汉理工大学,2009.
[6]NI Conpration.LabWindowsTM/CVI中的多线程技术[EB/OL].(2008 -01 -17)[2014 -06 -11]http://www.ni.com/white - paper.
[7]陈矫阳,陈楸,刘桓龙.基于LabWindows/CVI多线程数据采集的研究[J].科学技术与工程,2008,8(9):2459 -2461.
[8]周兵,江加和.基于LabWindows/CVI的虚拟测试平台研究与开发[J].研究与开发,2007(11):30-32.
[9]袁大伟.基于LabWindows/CVI的虚拟仪器系统的设计[D].哈尔滨:哈尔滨工程大学,2010.
[10]裴晓梅.基于LabWindows/CVI的涡流检测虚拟仪器系统的研究[D].西安:西安理工大学,2003.