1、LabWindows/CVI培训讲义培训讲义王丹丹王丹丹 2009.92/78目录目录u一、入门篇u二、基础篇u三、提高篇u四、技巧篇3/78一、入门篇一、入门篇1、CVI的特点2、学习CVI的几点建议3、CVI的开发环境简介4、构建一个简单程序4/781、CVI 的特点的特点什么是LabWindows/CVI?lLabWindows/CVI是针对测试测量领域的ANSI C开发环境l是把C语言与虚拟仪器的软件工具库结合起来的一种开发环境lC compiler for Virtual Instruments (CVI)5/781、CVI 的特点的特点CVI 的特点:l丰富的库函数,每个函数都提供
2、函数面板l专业化的图形界面设计控件l交互式编程环境l丰富的调试手段l提供部分仪器的驱动6/782、学习、学习CVI的几点建议的几点建议1. 要有良好的C语言基础2. 多上机实验3. 充分利用LabWindows/CVI的函数库4. 通过例子学习(CVISamples )5. 有明确的应用背景6. 程序要进行严格的调试,边写边调7. 养成良好的编程习惯,严格遵守编程规范7/783、CVI的开发环境的开发环境工作空间窗口:l工程目录区:左上角,显示工程架构l函数目录区:左下角,显示函数库和仪器库目录。双击 函数名,即可打开对应的函数面板。l窗口区:右半部分,编辑用户界面、源代码及函数面板。l输出区
3、:右半部下方,显示编译、运行及源代码等的错误信息。l运行区:右半部下方,显示变量、监视、内存等信息。(演示:打开一个工程文件,打开ATS项目编辑模块)8/783、 CVI的开发环境的开发环境CVI的文件类型l.cws 工作空间文件l.prj 工程文件l.uir 用户界面文件l.c C源代码文件l.h 头文件l.lib DLL导入库文件,外部静态库文件l.fp 函数库(函数数和函数面板)文件l.dll 动态链接库文件9/784、构建一个简单程序、构建一个简单程序1、建立工程文件2、创建用户界面文件,设置面板3、向面板添加控件,设置控件属性4、生成源代码文件 ,生成Main函数及各控件回 调函数,
4、编写代码5、运行和调试程序6、生成可执行文件和发布文件(演示:创建一个工程文件,演示各项操作)10/78二、基础篇二、基础篇1、基本控件的使用及编程2、弹出式面板3、创建菜单4、文件的读写操作5、常用函数11/781、基本控件的使用及编程基本控件的使用及编程l数值型数值型(Numeric):数值控件、颜色选择控件、数值滑动条等。12/781、基本控件的使用及编程、基本控件的使用及编程l文本型文本型(Text):字符串控件,文本信息,文本框。13/781、基本控件的使用及编程、基本控件的使用及编程l命令按钮命令按钮(Command Button):命令按钮、图片按钮。l双态按钮双态按钮(Togg
5、le Button):双态按钮,文本/图形按钮,单选/复选控件14/781、基本控件的使用及编程、基本控件的使用及编程l指示灯指示灯(LED)l二值开关二值开关(Binary Switch)15/781、基本控件的使用及编程、基本控件的使用及编程l列表控件列表控件(Ring):下拉列表,滑动列表,图形列表16/781、基本控件的使用及编程、基本控件的使用及编程l列表框和表格控件列表框和表格控件(Lists & Tables):列表框、树形控件、表格控件 (演示:.LabWindows3textlisttree)p列表框列表框(ListBox)17/781、基本控件的使用及编程、基本控件的使用及
6、编程p树形控件树形控件(Tree)18/781、基本控件的使用及编程、基本控件的使用及编程p表格控件表格控件(Table)19/781、基本控件的使用及编程、基本控件的使用及编程l图形控件图形控件(Graph):曲线图控件、带状图控件、数字图控件(演示:.LabWindows3graph)p曲线图控件曲线图控件(Graph)20/781、基本控件的使用及编程、基本控件的使用及编程p带状图控件带状图控件(Strip Chart)21/781、基本控件的使用及编程、基本控件的使用及编程Graph和Strip Chart的区别Graph是曲线图控件,用来显示图形数据,可以显示一条或多条图线。一条图线
7、包含曲线、数据点、几何图形、图片或字符串。支持缩放、移动。 Strip Chart是带状图控件,用来实时显示图形数据,可以包含一个或多个同时刷新的曲线,每一条曲线可以代表一个连续测量的数据通道。但Strip Chart不支持绘制几何图形、光标、缩放和移动等功能。22/781、基本控件的使用及编程、基本控件的使用及编程p数字图控件数字图控件(Digital Chart)23/781、基本控件的使用及编程、基本控件的使用及编程l定时器控件定时器控件(Timer)l画布控件画布控件(Canvas)24/781、基本控件的使用及编程、基本控件的使用及编程l分隔栏控件分隔栏控件(Splitter)25/
8、781、基本控件的使用及编程、基本控件的使用及编程l分页控件分页控件(Tab)26/781、基本控件的使用及编程、基本控件的使用及编程l装饰控件装饰控件(Decoration)l图片控件图片控件(Picture)27/782、弹出式面板、弹出式面板lMessage Popup:输出信息对话框 (演示:.LabWindows3弹出对话框)lGeneric Message Popup:在弹出式面板中显示一个设定的信息,同时可输入信息字符串,并且最多可设置3个按钮28/782、弹出式面板、弹出式面板lPrompt Popup:提示式信息弹出面板要求用户进行输入操作lConfirm Popup:确认信
9、息弹出面板,要求用户选择操作29/782、弹出式面板、弹出式面板lFile Select Popup:文件路径选择弹出面板30/782、弹出式面板、弹出式面板lGraph Popup:绘图控件弹出面板,包含4种: X Graph Popup、Y Graph Popup、XY Graph Popup、Waveform Graph Popup31/783、创建菜单、创建菜单l选择菜单Edit-Menu Bars,可弹出菜单栏列表对话框,在这个界面可创建和编辑菜单栏,右图为菜单栏编辑对话框(演示:ats模块)32/784、文件的读写操作、文件的读写操作主要介绍ini文件的读写操作l首先在工程中添加i
10、nifile.fp函数库,路径为.Program FilesNational InstrumentsCVI85toolslibtoolboxinifile.fpl然后,在函数目录区,Instruments文件夹下即新增了Reading/Writing .ini-Style Files类函数,使用此类函数进行ini文件的操作。l此类文件按节划分,每节中包含多个项目,各节各项目均相互独立,便于处理。编程时需注意修改某节某项目时不能破坏其他内容。(演示:.LabWindows11ini,或ATS测试模块)33/785、常用函数、常用函数1、面板操作函数、面板操作函数 (Libraries-User
11、Interface Library-Panels)lNewPanel创建面板lLoadPanel载入面板lDisplayPanel显示面板lDiscardPanel释放面板lHidePanel隐藏面板lGetPanelAttribute获取面板属性lSetPanelAttribute设置面板属性l 34/785、常用函数、常用函数2、通用控件操作函数、通用控件操作函数(Libraries-User Interface Library-Controls/Graphs/Strip Charts-General Functions)lGetCtrlVal获取控件值 lSetCtrlVal设置控件值l
12、GetCtrlAttribute获取控件属性值lSetCtrlAttribute设置控件属性值这4个函数对大部分控件均有效,是最常用的函数。35/785、常用函数、常用函数3、特定控件操作函数、特定控件操作函数(Libraries-User Interface Library-Controls/Graphs/Strip Charts-.)l列表控件函数l文本框控件函数l图形控件函数l定时器控件函数l表格控件函数l分页控件函数l 36/785、常用函数、常用函数4、菜单操作函数、菜单操作函数(Libraries-User Interface Library-Menu Structures)lLo
13、adMenuBar加载菜单栏 lDiscardMenuBar释放菜单栏l 5、用户界面管理函数、用户界面管理函数(Libraries-User Interface Library-User Interface Management)lRunUserInterface运行用户界面lQuitUserInterface退出用户界面l 37/785、常用函数、常用函数6、字符串操作字符串操作(Libraries-ANSI C-String Handling-.)lstrlen获取字符串的长度lstrcpy, strncpy字符串拷贝(全部,前n个字符)lstrcmp, strncmp, stricmp
14、字符串比较(全部,前n个字符,不区分大小写比较)lstrcat, strncat字符串连接(全部,前n个字符)lstrchr在字符串中查询某个字符lstrstr在字符串中查询字符串l 38/785、常用函数、常用函数7、数据格式化数据格式化(Libraries-ANSI C-String Handling-.) (Libraries-ANSI C-General Utilities.) (Libraries-Formatting and I/O Library-Data Formatting-.)lFmt(strValue,%s%x,byteValue); lstrtol, strtoll,
15、strtod, pParaValuei.ucvalue = strtol (inValueArrayi,&endStr , 16); latoi, atol, atof, pParaValuei.fvalue = atof(inValueArrayi); lsprintf(tempString,%s=%d,tempName,tempValue);lsscanf(constraint,(VarID=%d)%s),&varID,valueStr); l39/78三、提高篇三、提高篇1、串口通讯2、动态链接库技术3、Windows SDK和API技术4、ActiveX技术5、数据库技术6、多线程编程
16、40/781、串口通讯、串口通讯1、打开配置和关闭、打开配置和关闭lint OpenComConfig (int COM_Port, char Device_Name, long Baud_Rate, int Parity, int Data_Bits, int Stop_Bits, int Input_Queue_Size, int Output_Queue_Size);i = OpenComConfig (1, “”, 14400, 0, 7, 1, 512, 512);lint CloseCom (int COM_Port);i = CloseCom (1);2、读写、读写lint Co
17、mRd (int COM_Port, char Buffer, int Count);i = ComRd (1, buf, 128);lint ComWrt (int COM_Port, char Buffer, int Count);i = ComWrt (1, buf, 128);lint ComRdByte (int COM_Port);lint ComWrtByte (int COM_Port, int Byte);3、控制、控制lint FlushInQ (int COM_Port);lint FlushOutQ (int COM_Port);41/781、串口通讯、串口通讯4、状态
18、查询lint GetInQLen (int COM_Port);i = GetInQLen (1);lint GetOutQLen (int COM_Port);i = GetOutQLen (1);5、事件处理lint InstallComCallback (int COM_Port, int Event_Mask, int Notify_Count, int Event_Character, ComCallbackPtr Callback_Function, void *Callback_Data);notifyCount = 50; / Wait for at least 50 byte
19、s in queue eventChar = 13; / Wait for LF eventMask = LWRS_RXFLAG | LWRS_RECEIVE; InstallComCallback (comport, eventMask, notifyCount, eventChar, ComCallbackFunction, NULL);42/782、动态链接库技术、动态链接库技术1.静态链接和动态链接静态链接是在链接时(生成EXE文件)把程序编译后的代码插入到可执行文件中动态链接时,链接器只插入数据表格,在可执行文件运行前装入内存2.DLL的优点节省内存易于版本升级易于复杂程序管理多语言
20、(字符)支持独立于编程语言43/782、动态链接库技术、动态链接库技术3.DLL和EXE的区别内部结构相同DLL即使装载到内存中时,也不是运行程序。它只是其它程序可以调用的过程与函数的集合。这些过程与函数使用了调用程序的堆栈。 库不能建立自己的堆栈,而使用调用它的主程序的堆栈。 DLL的任何内存分配或建立的任何全局数据都驻留在主程序的地址空间中 44/782、动态链接库技术、动态链接库技术4.创建DLL按正常程序编写DLL源文件函数每个DLL都要有一个入口函数DllMain(Borland 编译器使用DllEntryPoint实现相同的功能),用它代替WinMain函数或Main函数把需要外部
21、调用的函数和变量导出l头文件法(在头文件中定义)int DLLSTDCALL MyIntFunc (void);char *DLLSTDCALL MyStringFunc (void);l导出关键字法(在源文件中定义)int DLLEXPORT DLLSTDCALL MyFunc (int parm) int DLLEXPORT myVar = 0;45/782、动态链接库技术、动态链接库技术在工程文件窗口选择BuildTarget TypeDynamic Link Library 如果使用头文件法,在工程文件窗口选择BuildTarget Settings 并在exports中选择Chang
22、e按钮,出现DLL Export Option界面中选择导出的头文件创建可调试DLL或发布DLL。46/782、动态链接库技术、动态链接库技术创建DLL的建议:l在需要导出的所有函数的声明和定义中使用DLLSTDCALL宏,不要导出带有可变数量参数的函数。l不要在DLL中导出变量。为每一个需要导出的变量创建一个读取它的值的函数,或者返回这个变量指针的函数。l在头文件中不要使用导入关键字。47/782、动态链接库技术、动态链接库技术创建无界面的动态链接库:int _stdcall DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvRe
23、served)switch (fdwReason)case DLL_PROCESS_ATTACH:/装在DLLif (InitCVIRTE (hinstDLL, 0, 0) = 0)return 0; / out of memory break;case DLL_PROCESS_DETACH:/卸载DLLCloseCVIRTE ();break;return 1;48/782、动态链接库技术、动态链接库技术创建有界面的动态链接库:int _stdcall DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)switc
24、h (fdwReason)case DLL_PROCESS_ATTACH:/装在DLLif (InitCVIRTE (hinstDLL, 0, 0) = 0)return 0;/ out of memory break;case DLL_PROCESS_DETACH:/卸载DLL/如果cvirte.dll(CVI运行时库)已经被释放,则不能调用CVI函数if (!CVIRTEHasBeenDetached()/释放用户界面资源DiscardUIObjectsForDLL ();CloseCVIRTE ();break;return 1;49/782、动态链接库技术、动态链接库技术5.调试DLL
25、在调用DLL的工程文件中调试l打开调用DLL的工程文件l把编译好的DLL导引库文件(.lib)加入到调用DLL的工程文件l打开DLL源文件,不要加入到工程文件l在DLL源文件中设置断点进行调试在DLL工程文件中调试l打开DLL工程文件l在工程文件窗口选择RunSelect External Process ,输入外部调用DLL的可执行文件名l在DLL源文件中设置断点进行调试50/782、动态链接库技术、动态链接库技术6.使用DLLLabWindows/CVI通过标准的32位导入库连接到DLL 链接导入库的方式:l把它列入到在工程中l用一个仪器驱动程序或用户库的 .fp文件把它联系起来l通过调用
26、LoadExternalModule或Windows SDK LoadLibary函数动态载入如果把导入库列到工程文件中,或者用一个仪器驱动程序或用户库的 .fp文件把它联系起来,LabWindows/CVI将把导入库静态载入到可执行文件或DLL中,这种方式又叫隐式加载。 通过调用LoadExternalModule或Windows SDK LoadLibary函数动态载入导入库,这种方式又叫做显示加载。不能直接把DLL的路径名传递给LoadExternalModule,而是必须传递DLL导入库的路径名。51/782、动态链接库技术、动态链接库技术操作系统使用标准DLL搜索算法查找子DLL,搜
27、索次序如下:l用户载入程序的路径l当前工作路径lWindows 98/95下是Windows system目录,Windows NT/2000是Windows system32和system目录。lWindows 目录l列在PATH环境变量中的目录使用LoadExternalModule显式加载DLLl在需要的时候调入,节省内存l反复调用影响效率52/783、 Windows SDK和和API技术技术1.什么是Windows SDKWindows SDK:Windows Software Development Kit,Windows软件开发工具 Windows SDK程序设计就是Window
28、s API方式的Windows程序设计 Windows API:Windows Application Programming Interface,Windows应用程序编程接口 API就是一系列的例程,应用程序通过调用这些例程来请求操作系统完成一些低级服务53/783、 Windows SDK和和API技术技术2.使用SDK在LabWindows/CVI中安装Windows SDK在程序中加入Windows SDK头文件, Windows.h在工程文件中加入函数所在的导入库,LabWindows/CVI自动载入4个导入库lkernel32.lib 管理内存、进程和线程lgdi32.lib绘图
29、和显示文本luser32.lib执行用户界面任务(如创建窗口和发送消息)ladvapi.lib对象安全、注册表管理和事件记录Windows.h在其它头文件的前面,避免定义冲突54/783、 Windows SDK和和API技术技术3.程序举例显示帮助文档 HtmlHelp(NULL,.binAN8060 HELP.CHM,HH_DISPLAY_TOPIC,(DWORD)第十一章 项目编辑.mht);弹出提示信息 MessageBox(FindWindow(LPCTSTR)NULL,(LPCTSTR)“产品测试”),VERSION,“关于产品测试,MB_OK|MB_ICONASTERISK);加
30、载动态链接库dllHandle=LoadLibrary(dllFileName); 55/784、ActiveX技术技术1.什么是ActiveXActiveX是使软件组件能够在系统环境中交互作用而与创建组件的语言无关的一套封装技术 ActiveX控件要在Windows中被正确使用,必须首先将控件文件.OCX复制到Windows/System32下,然后进行注册 由于ActiveX控件涉及到注册,经常容易出现问题,一般不推荐使用注册方法:开始菜单中,点击“开始-运行.”,在运行对话框中输入以下命令:lregsvr32 : 注册一个ActiveX控件lRegsvr32/u :解除某ActiveX的
31、控件56/784、ActiveX技术技术2.CVI中ActiveX的调用在用户界面面板中空白处右击,选择ActiveX选项 在弹出的ActiveX对象对话框中选择需要的控件选项,确定后面板上就会出现添加的ActiveX控件 在ActiveX控件上右击,选择Generate ActiveX Control Driver,会弹出ActiveX控件生成向导,操作完成后,系统自动产生可识别的程序文件,一般包括.c、.h、.fp、.obj、.sub文件。其中.fp文件就是函数面板文件,自动添加到工程目录区Instrment Files文件夹下,同时函数目录区也自动添加了该控件对应的函数,可供编程57/7
32、84、ActiveX技术技术3.ActiveX应用实例l常用的ActiveX控件有: TimePicker 、Calendar、Chart、ProgressBar等等。l以日期设置控件TimePicker为例做详细说明,选择Microsoft Date and Time Picker Control, version 6.0项。(演示:.LabWindows12timepicker)58/785、数据库技术、数据库技术1.数据库的基础知识数据库由组织好的数据集组成,表现为表的形式。表的结构通过记录和字段构成的二维形式来实现(其中,记录又称为行,字段又称为列)。数据库中每个表必须有一个唯一的名字
33、。类似地,表中每个字段必须有一个唯一的名字。 表里的数据本身并无顺序。在用SELECT语句从表中回收数据时,发生排序、分组和其它数据操作。表中每列都有一个数据类型。59/785、数据库技术、数据库技术2.结构化查询语言(Structured Query Language, SQL) 利用SQL即可实现对数据库的各种交互操作,SQL是关系型数据库管理系统的标准语言。一些经常用到的SQL命令:lCREATE TABLE创建一个新的表,指定每列的名字和数据类型。lSELECT回收表中符合特定条件的所有行。lINSERT将一个新的记录添加到表中,接着可以对行赋值。lUPDATE改变指定行里符合特定条件
34、的所有列的值。lDELETE删除符合特定条件的所有行。60/785、数据库技术、数据库技术3.SQL Toolkit数据库操作基本步骤 步骤1. 连接数据库执行SQL语句前,必须建立数据库连接。LabWindows/CVI SQL工具包支持单个数据库或多个数据库的多个同步连接。步骤2. 激活SQL连接用LabWindows/CVI SQL工具包可以按几个方式激活SQL语句,如自动SQL和显式SQL。l自动SQL为用户构造SQL语句。自动SQL只能构造简单的SELECT和CREATE TABLE语句。l显式SQL必须将SQL语句传递到函数中。对更复杂的SELECT语句或其它类型的语句,应采用显式
35、SQL。l更多操作控制由相关高级函数执行。61/785、数据库技术、数据库技术步骤3. 处理SQL语句l通常只有SQL SELECT语句要求进一步处理。SELECT语句是LabWindows/CVI SQL工具包的重要组件。用SELECT语句可进行下列数据库操作:从表中获取纪录更新表中的纪录在表里创建新纪录l使用SELECT语句必须将选择的字段绑定到程序中的变量上。接着用取值函数获取所选记录。每次调用取值函数,工具包将列变量拷贝到在程序中绑定的变量。更新行或创建行时还可用绑定的变量,即更新行或创建行时将新值拷贝到绑定的变量并随后调用适当的函数,将程序中的数据添加到数据库对应的字段。62/785
36、、数据库技术、数据库技术步骤4. 断开SQL连接lSQL操作结束后,应断开SQL连接来释放系统资源。这个步骤在任何方向上取值时都是特别重要的,它可保证工具包适当地关闭并删除临时登记文件。步骤5. 断开数据库l全部数据库操作结束时,应断开通过SQL建立的数据库连接,以适时地释放系统资源。63/785、数据库技术、数据库技术写数据库例程:写数据库例程:hdbc = DBConnect (ATS_DATA_DSN); /连接数据库 DBSetDatabase(hdbc, dataBasePath); /设置数据源hstat = DBActivateSQL(hdbc,SELECT * FROM Tes
37、tItemList); /激活SQL连接if (hstat ItemID = DBNumberOfRecords (hstat); /获得记录的总数,作为当前要添加的测试项目的ID DBCreateRecord(hstat); resCode =DBPutColChar(hstat,1,pParaProgV-ItemName,);resCode =DBPutColInt (hstat,2,pParaProgV-ItemID);DBPutRecord(hstat);resCode = DBDeactivateSQL(hstat); /断开SQL连接 DBDisconnect(hdbc); /断开
38、数据库64/785、数据库技术、数据库技术读数据库例程:读数据库例程:hdbc = DBConnect (ATS_DATA_DSN); /连接数据库 DBSetDatabase(hdbc, dataBasePath); /设置数据源hstat = DBPrepareSQL (hdbc, SELECT * FROM TestItemList WHERE TIName=?); /激活SQL连接 if (hstat ItemID);resCode = DBGetColCharBuffer(hstat,2,pParaProgV-ItemRelateDevices,128,); resCode = DB
39、ClosePreparedSQL (hstat); /断开SQL连接 DBDisconnect(hdbc); /断开数据库65/786、多线程编程、多线程编程1.线程和进程进程常被定义为应用程序的运行实例。线程是指进程内部的可独立执行的单元,是操作系统对系统资源的基本调度单位。 每个进程至少拥有一个线程,这个线程被称为主线程(main thread) 。一个进程也可以拥有多个线程,同属于一个进程的所有线程都共享进程的虚拟地址空间,线程之间可以共享进程的全局数据和资源。 66/786、多线程编程、多线程编程2. 多线程程序(Multi-Threading )多线程程序就是在同一个时间段内至少有两
40、个线程在执行程序代码的程序。 在多线程程序中,程序自己让操作系统创建主线程之外的其它线程,这些线程被称作次线程。(secondary thread) 次线程和主线程的主要差别就在于线程在何处开始执行。主线程一般开始执行程序的main或WinMain函数;程序开发者确定每一个次线程开始执行的函数。操作系统允许一个特定线程执行的时段被称作一个时间片(time-slice)。操作系统暂停一个线程的执行,转而允许另一个线程执行它的时间片的行为被称作线程切换(thread-switch)。 67/786、多线程编程、多线程编程3. 多线程的优点改善吞吐量,避免阻塞 提高响应速度和更有效的后台处理 减少运
41、行过程和用户界面的相互影响最大程度利用多处理器的性能 68/786、多线程编程、多线程编程4. 两种多线程机制1.异步定时器 适用于在固定时间间隔内执行的任务 调用toolslib.fp函数NewAsyncTimer,把想要在次线程中执行的函数名称传递给它,并且设定每次函数执行的时间间隔 lint NewAsyncTimer (double Interval, int Count, int Initial_State, void *Event_Function, void *Callback_Data);lint CVICALLBACK FunctionName (int reserved,
42、int timerId, int event,void *callbackData, int eventData1, int eventData2);toolslib库使用一个多媒体定时器线程执行注册在一个程序里的所有异步定时器回调,因此,如果想要在程序里并行执行多个函数, 建议使用线程池函数69/786、多线程编程、多线程编程2.线程池 (thread pools) 适用于需要不连续地执行多次或在循环中执行的任务 调用实用库(Utility Library)中的CmtScheduleThreadPoolFunction函数,把想要在次线程中执行的函数名传递给它,线程池调度这个函数在它的其中一
43、个线程中运行。 int CmtScheduleThreadPoolFunction (int poolHandle, ThreadFunctionPtr threadFunction, void *threadFunctionData, int *threadFunctionID);int CVICALLBACK ThreadFunction (void *functionData);70/786、多线程编程、多线程编程int CVICALLBACK DataAcqThreadFunction (void *functionData);int main(int argc, char *argv
44、)int panelHandle;int functionId;if (InitCVIRTE (0, argv, 0) = 0)return -1; /*内存溢出 */if (panelHandle = LoadPanel(0, DAQDisplay.uir, PANEL) 0)return -1;DisplayPanel (panelHandle);CmtScheduleThreadPoolFunction (DEFAULT_THREAD_POOL_HANDLE, DataAcqThreadFunction, NULL, &functionId);RunUserInterface ();Di
45、scardPanel (panelHandle);CmtWaitForThreadPoolFunctionCompletion (DEFAULT_THREAD_POOL_HANDLE, functionId, 0);return 0;int CVICALLBACK DataAcqThreadFunction (void *functionData)while (!quit) Acquire(.);Analyze(.);return 0;71/786、多线程编程、多线程编程5. 多线程间的数据保护1.为什么要保护数据 如果一个变量被多个线程访问,必须保护它以确保它的数值不被破坏 2.需要保护的数
46、据种类 在程序中只有多个线程访问的变量才必须需要保护。全局变量、静态局部变量和动态分配的内存位于公共内存区域,可以被程序中的所有线程访问,存放在这类内存区域中的数据必须保护以免多个线程同时访问。函数参数和非静态局部变量位于堆栈中,操作系统为每个线程分配了单独的堆栈,每个线程因此获得自己的参数和非静态局部变量的拷贝,所以参数和非静态局部变量不需要保护。72/786、多线程编程、多线程编程3.如何保护数据 线程锁(thread lock)l简单,容易出现阻塞和死锁,效率低线程安全变量(thread safe variables)l安全,不容易出错线程安全队列(thread safe queues)
47、 l线程安全队列是为多个线程之间传递大批数据提供的一种机制。通常在程序中有一个线程产生一批数据,另一个线程需要操作这批数据的情况下会用到线程安全队列。l例如,程序的一个线程用DAQ采集数据,而另一个线程分析或者在LabWindows/CVI的用户界面显示这些数据。 73/786、多线程编程、多线程编程int queue;int panelHandle;int main (int argc, char *argv)if (InitCVIRTE (0, argv, 0) = 0)return -1; /* out of memory */if (panelHandle = LoadPanel(0,
48、 DAQDisplay.uir“, PANEL) 0)return -1;/*创建一个容纳1000个双精度数据的队列,需要的时候还可以扩大*/CmtNewTSQ(1000, sizeof(double), OPT_TSQ_DYNAMIC_SIZE, &queue);CmtInstallTSQCallback (queue, EVENT_TSQ_ITEMS_IN_QUEUE, 500,QueueReadCallback, 0, CmtGetCurrentThreadID(), NULL);CmtScheduleThreadPoolFunction (DEFAULT_THREAD_POOL_HAN
49、DLE, DataAcqThreadFunction, NULL, NULL);DisplayPanel (panelHandle);RunUserInterface();.return 0;void CVICALLBACK QueueReadCallback (int queueHandle, unsigned int event,int value, void *callbackData)double data500;CmtReadTSQData (queue, data, 500, TSQ_INFINITE_TIMEOUT, 0); 74/786、多线程编程、多线程编程6. 线程的优先级
50、1.Windows允许定义每个进程和线程中的工作的相对重要性,也就是优先级。2.如果设定一个进程或进程中的线程一个较高的优先级,这个进程或线程就比其它较低优先级的线程获得更大的优先权。这意味着当有多个线程准备运行时,允许最高优先级的线程先运行。3.Windows把优先级分成若干等级,所有在同一个进程中的线程拥有相同的优先等级。进程中的每一个线程都有一个相对于这个进程的优先等级的优先级。 75/786、多线程编程、多线程编程7. 线程局部变量1.线程局部变量和全局变量有些相似,它们都可以被任何线程访问。2.线程局部变量为每一个线程的访问保存独立的数值,而全局变量为所有线程保存单一的数值。3.程序