




已阅读5页,还剩15页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
021 65557838 800 820 3622 china info National Instruments LabWindows CVI 中的多线程技中的多线程技术术 概览概览 多任务 多线程和多处理这些术语经常被交替地使用 但是它们在本质上是不同的概念 多任务是指操作系统 具有在任务间快速切换使得这些任务看起来是在同步执行的能力 在一个抢占式多任务系统中 应用程序可以 随时被暂停 使用多线程技术 应用程序可以把它的任务分配到单独的线程中执行 在多线程程序中 操作系 统让一个线程的代码执行一段时间 被称为时间片 后 会切换到另外的线程继续运行 暂停某个线程的运行 而开始执行另一个线程的行为被称为线程切换 通常情况下 操作系统进行线程切换的速度非常快 令用户觉 得有多个线程在同时运行一样 多处理指的是在一台计算机上使用多个处理器 在对称式多处理 SMP 系统 中 操作系统自动使用计算机上所有的处理器来执行所有准备运行的线程 借助于多处理的能力 多线程应用 程序可以同时执行多个线程 在更短的时间内完成更多的任务 单线程应用程序移植到多核处理器上运行不会获得性能上的改进 这是因为它们只能在其中一个处理器上运 行 而不能像多线程应用程序那样在所有的处理器上同时运行 而且单线程应用程序需要承受操作系统在处理 器间切换所需要的开销 为了在多线程操作系统和 或多处理器计算机上获得最优异的性能 我们必须使用多 线程技术来编写应用程序 进行多线进行多线程编程的原因程编程的原因 在程序中使用多线程技术的原因主要有四个 最常见的原因是多个任务进行分割 这些任务中的一个或多个是 对时间要求严格的而且易被其他任务的运行所干涉 例如 进行数据采集并显示用户界面的程序就很适合使用 多线程技术实现 在这种类型的程序中 数据采集是时间要求严格的任务 它很可能被用户界面的任务打断 在 LabWindows CVI 程序中使用单线程方法时 程序员可能需要从数据采集缓冲区读出数据并将它们显示到用户 界面的曲线上 然后处理事件对用户界面进行更新 当用户在界面上进行操作 如在图表上拖动光标 时 线 程将继续处理用户界面事件而不能返回到数据采集任务 这将导致数据采集缓冲区的溢出 而在 LabWindows CVI 程序中使用多线程技术时 程序员可以将数据采集操作放在一个线程中 而将用户界面处理放在另一个线程中 这样 在用户对界面进行操作时 操作系统将进行线程切换 为数据采集线程提供完成任务所需的时间 在程序中使用多线程技术的第二个原因是程序中可能需要同时进行低速的输入 输出操作 例如 使用仪器来 测试电路板的程序将从多线程技术中获得显著的性能提升 在 LabWindows CVI 程序中使用单线程技术时 程序 员需要从串口发送数据 初始化电路板 程序需要等待电路板完成操作之后 再去初始化测试仪器 必须要 等待测试仪器完成初始化之后 再进行测量 在 LabWindows CVI 程序中使用多线程技术时 你可以使用另一 个线程来初始化测试仪器 这样 在等待电路板初始化的同时等待仪器初始化 低速的输入 输出操作同时进 行 减少了等待所需要的时间总开销 在程序中使用多线程技术的第三个原因是借助多处理器计算机来提高性能 计算机上的每个处理器可以都执行 一个线程 这样 在单处理器计算机上 操作系统只是使多个线程看起来是同时执行的 而在多处理器计算机 上 操作系统才是真正意义上同时执行多个线程的 例如 进行数据采集 将数据写入磁盘 分析数据并且在 021 65557838 800 820 3622 china info National Instruments 用户界面上显示分析数据 这样的程序很可能通过多线程技术和多处理器计算机运行得到性能提升 将数据写 到磁盘上和分析用于显示的数据是可以同时执行的任务 在程序中使用多线程技术的第四个原因是在多个环境中同时执行特定的任务 例如 程序员可以在应用程序中 利用多线程技术在测试舱进行并行化测试 使用单线程技术 应用程序需要动态分配空间来保存每个舱中的测 试结果 应用程序需要手动维护每个记录及其对应的测试舱的关系 而使用多线程技术 应用程序可以创建独 立的线程来处理每个测试舱 然后 应用程序可以使用线程局部变量为每个线程创建测试结果记录 测试舱与 结果记录间的关系是自动维护的 使应用程序代码得以简化 选择合适的操作系统选择合适的操作系统 微软公司的 Windows 9x 系列操作系统不支持多处理器计算机 所以 你必须在多处理器计算机上运行 Windows Vista XP 2000 NT 4 0 系统来享受多处理器带来的好处 而且 即使在单处理器计算机上 多线程程序在 Windows Vista XP 2000 NT 4 0 上的性能也比在 Windows 9x 上好 这要归功于 Windows Vista XP 2000 NT 4 0 系统有着更为高效 的线程切换技术 但是 这种性能上的差别在多数多线程程序中体现得并不是十分明显 对于程序开发 特别是编写和调试多线程程序而言 Windows Vista XP 2000 NT 4 0 系列操作系统比 Windows 9x 系 列更为稳定 当运行操作系统代码的线程被暂停或终止的时候 操作系统的一些部分有可能出于不良状态中 这种情况使得 Windows 9x 操作系统崩溃的几率远远高于 Windows Vista XP 2000 NT 4 0 系统的几率 所以 NI 公司 推荐用户使用运行 Windows Vista XP 2000 NT 4 0 操作系统的计算机来开发多线程程序 LabWindows CVI中的多线程技术简介中的多线程技术简介 NI LabWindows CVI 软件自二十世纪九十年代中期诞生之日起就支持多线程应用程序的创建 现在 随着多核 CPU 的广泛普及 用户可以使用 LabWindows CVI 来充分利用多线程技术的优势 与 Windows SDK threading API Windows 软件开发工具包线程 API 相比 LabWindows CVI 的多线程库提供了以下 多个性能优化 Thread pools 帮助用户将函数调度到独立的线程中执行 Thread pools 处理线程缓存来最小化与创建和销 毁线程相关的开销 Thread safe queues 对线程间的数据传递进行了抽象 一个线程可以在另一个线程向队列写入数据的同 时 从队列中读取数据 Thread safe variables 高效地将临界代码段和任意的数据类型结合在一起 用户可以调用简单的函数来 获取临界代码段 设定变量值 然后释放临界代码段 Thread locks 提供了一致的 API 并在必要时自动选择合适的机制来简化临界代码段和互斥量的使用 例 如 如果需要在进程间共享互斥锁 或者线程需要在等待锁的时候处理消息 LabWindows CVI 会自动使 用互斥量 临界代码段使用在其它场合中 因为它更加高效 Thread local variables 为每个线程提供变量实例 操作系统对每个进程可用的线程局部变量的数量进行 了限制 LabWindows CVI 在实现过程中对线程局部变量进行了加强 程序中的所有线程局部变量只使用 一个进程变量 可以在 Utility Library Multithreading 下的 LabWindows CVI 库函数树状图中找到所有的多线程函数 021 65557838 800 820 3622 china info National Instruments 在在 LabWindows CVI 的辅助线程中运行代码的辅助线程中运行代码 单线程程序中的线程被称为主线程 在用户告诉操作系统开始执行特定的程序时 操作系统将创建主线程 在 多线程程序中 除了主线程外 程序还通知操作系统创建其他的线程 这些线程被称为辅助线程 主线程和辅 助线程的主要区别在于它们开始执行的位置 操作系统从 main 或者 WinMain 函数开始执行主线程 而由开发人 员来指定辅助线程开始执行的位置 在典型的 LabWindows CVI 多线程程序中 开发者使用主线程来创建 显示和运行用户界面 而使用辅助线程来 进行其它时间要求严格的操作 如数据采集等 LabWindows CVI 提供了两种在辅助进程中运行代码的高级机制 这两种机制是线程池 thread pools 和异步定时器 线程池适合于执行若干次的或者一个循环内执行的任务 而异步定时器适合于定期进行的任务 使用线程池使用线程池 为了使用 LabWindows CVI 的线程池在辅助线程中执行代码 需要调用 Utility Library 中的 CmtScheduleThreadPoolFunction 函数 将需要在辅助线程中运行的函数名称传递进来 线程池将这个函数调度到 某个线程中执行 根据配置情况和当前的状态 线程池可能会创建新的线程来执行这个函数 也可能会使用已 存在的空闲进程执行函数或者会等待一个活跃的线程变为空闲然后使用该线程执行预定的函数 传递给 CmtScheduleThreadPoolFunction 的函数被称为线程函数 线程池中的线程函数可以选择任意的名称 但是必须遵 循以下原型 int CVICALLBACK ThreadFunction void functionData 下面的代码显示了如何使用 CmtScheduleThreadPoolFunction 函数在辅助进程中执行一个数据采集的线程 int CVICALLBACK DataAcqThreadFunction void functionData int main int argc char argv int panelHandle int functionId if InitCVIRTE 0 argv 0 0 return 1 out of memory if panelHandle LoadPanel 0 DAQDisplay uir PANEL data safeBufPtr count val safeBufPtr count ReleasePointerToSafeBuf 检测对检测对 GetPointerToVarName 的不匹配调的不匹配调用用 可以通过 DefineThreadSafeScalarVar 和 DefineThreadSafeArrayVar 的最后一个参数 maxGetPointerNestingLevel 来 指定最大数目的嵌套调用 通常可以把这个参数设为 0 这样 GetPointerToVarName 在检测到同一线程中对 GetPointerToVarName 的两次连续调用而中间没有对 ReleasePointerToVarName 进行调用时 就会报出一个运行错 误 例如 下面的代码在第二次执行的时候会报出 run time error 的错误 因为它忘记了调用 ReleasePointerToCount 函数 int IncrementCount void int countPtr countPtr GetPointerToCount run time error on second execution countPtr Missing call to ReleasePointerToCount here return 0 如果代码中必须对 GetPointerToVarName 进行嵌套调用时 那么可将 maxGetPointerNestingLevel 参数设为一个大 于零的整数 例如 下面的代码将 maxGetPointerNestingLevel 参数设定为 1 因此它允许对 GetPointerToVarName 进行一级嵌套调用 DefineThreadSafeScalarVar int Count 1 int Count void int countPtr countPtr GetPointerToCount countPtr 021 65557838 800 820 3622 china info National Instruments DoSomethingElse calls GetPointerToCount ReleasePointerToCount return 0 void DoSomethingElse void int countPtr countPtr GetPointerToCount nested call to GetPointerToCount do something with countPtr ReleasePointerToCount 如果不知道 GetPointerToVarName 的最大嵌套级别 那么请传递 TSV ALLOW UNLIMITED NESTING 来禁用对 GetPointerToVarName 函数的不匹配调用检查 线程安全队线程安全队列列 使用 LabWindows CVI Utility Library 的线程安全队列 可以在线程间安全地传递数据 当需要用一个线程来采集数 据而用另一个线程来处理数据时 这种技术非常有用 线程安全队列在其内部处理所有的数据锁定 通常说来 应用程序中的辅助线程获取数据 而主线程在数据可用时读取数据然后分析并 或显示数据 下面的代码显示 了线程如何使用线程安全队列将数据传递到另外一个线程 在数据可用时 主线程利用回调函数来读取数据 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 DAQDisplay uir PANEL 0 return 1 create queue that holds 1000 doubles and grows if needed CmtNewTSQ 1000 sizeof double OPT TSQ DYNAMIC SIZE CmtInstallTSQCallback queue EVENT TSQ ITEMS IN QUEUE 500 QueueReadCallback 0 CmtGetCurrentThreadID NULL CmtScheduleThreadPoolFunction DEFAULT THREAD POOL HANDLE DataAcqThreadFunction NULL NULL DisplayPanel panelHandle RunUserInterface return 0 021 65557838 800 820 3622 china info National Instruments void CVICALLBACK QueueReadCallback int queueHandle unsigned int event int value void callbackData double data 500 CmtReadTSQData queue data 500 TSQ INFINITE TIMEOUT 0 避免死锁避免死锁 当两个线程同时等待对方持有的线程锁定对象时 代码就不能继续运行了 这种状况被称为死锁 如果用户界 面线程发生死锁 那么它就不能响应用户的输入 用户必须非正常地结束程序 下面的例子解释了死锁是如何 发生的 线程 1 调用函数来获取线程锁 A 线程 1 无线程锁 线程 2 无线程锁 线程 1 从获取线程锁的函数返回 线程 1 持有线程锁 A 线程 2 无线程锁 切换到线程 2 线程 1 持有线程锁 A 线程 2 无线程锁 线程 2 调用函数来获取线程锁 B 线程 1 持有线程锁 A 线程 2 无线程锁 线程 2 从获取线程锁的函数返回 线程 1 持有线程锁 A 线程 2 持有线程锁 B 线程 2 调用函数来获取线程锁 A 线程 1 持有线程锁 A 线程 2 持有线程锁 B 线程 2 由于线程 1 持有线程锁 A 而被阻塞 线程 1 持有线程锁 A 线程 2 持有线程锁 B 切换到线程 1 调用函数来获取线程锁 B 线程 1 持有线程锁 A 线程 2 持有线程锁 B 线程 1 调用函数来获取线程锁 B 线程 1 持有线程锁 A 线程 2 持有线程锁 B 线程 1 由于线程 2 持有线程锁 A 而被阻塞 线程 1 持有线程锁 A 线程 2 持有线程锁 B 与不对数据进行保护时产生的错误相似 由于程序运行的情况不同导致线程切换的时序不同 死锁错误间歇性 地发生 例如 如果直到线程 1 持有线程锁 A 和 B 后才切换到线程 2 那么线程 1 就可以完成工作而释放掉这 些线程锁 让线程 2 在晚些时候获取到 就像上面所说的那样 死锁现象只有在线程同时获取线程锁时才会发 生 所以你可以使用简单的规则来避免这种死锁 当需要获取多个线程锁对象时 程序中的每个线程都需要按 照相同的顺序来获取线程锁对象 下面的 LabWindows CVI Utility Library 函数获取线程锁对象 并且返回时并不释 放这些对象 CmtGetLock CmtGetTSQReadPtr CmtGetTSQWritePtr 注意事项注意事项 通常说来 不需要直接调用 CmtGetTSVPtr 函数 它是通过 DeclareThreadSafeVariable 宏创建的 GetPtrToVarName 函数调用的 因此 对于调用的 GetPtrToVarName 函数需要将它作为线程锁对象获取函数来对 021 65557838 800 820 3622 china info National Instruments 待 应该注意死锁保护的问题 The following Windows SDK functions can acquire thread locking objects without releasing them before returning Note This is not a comprehensive list 下面的 Windows SDK 函数可以获取线程锁对象但在返回时并不释放这些对象 注意 这不是完整的列表 EnterCriticalSection CreateMutex CreateSemaphore SignalObjectAndWait WaitForSingleObject MsgWaitForMultipleObjectsEx 监视和控制辅助线程监视和控制辅助线程 在把一个函数调度到独立的线程中运行时 需要对被调度函数的运行状态进行监视 为了获得被调度函数的运 行状态 调用 CmtGetThreadPoolFunctionAttribute 来获得 ATTR TP FUNCTION EXECUTION STATUS 属性的值 也可 以注册一个回调函数 线程池调用之后立即运行被调度的函数和 或开始运行后立即由线程池调用 如果需要 注册这样的回调函数 必须使用 CmtScheduleThreadFunctionAdv 来对函数进行调度 通常说来 辅助进程需要在主线程结束程序前完成 如果主线程在辅助线程完成之前结束 那么辅助线程将不 能够将分配到的资源清理掉 同时 可能导致这些辅助线程所使用的库函数也不能被正确清除 可以调用 CmtWaitForThreadPoolFunctionCompletion 函数来安全地等待辅助线程结束运行 然后允许主线程结束 在一些例子中 辅助线程函数必须持续完成一些工作直到主线程让它停止下来 在这类情况下 辅助线程通常 在 while 循环中完成任务 while 循环的条件是主线程中设定的整数变量 当主线程需要告知辅助线程停止运行 时 将其设为非零整数 下面的代码显示了如何使用 while 循环来控制辅助线程何时结束执行 volatile int quit 0 int main int argc char argv int functionId CmtScheduleThreadPoolFunction DEFAULT THREAD POOL HANDLE ThreadFunction NULL This would typically be done inside a user interface Quit button callback quit 1 CmtWaitForThreadPoolFunctionCompletion DEFAULT THREAD POOL HANDLE functionId 0 return 0 int CVICALLBACK ThreadFunction void functionData while quit 021 65557838 800 820 3622 china info National Instruments return 0 注意事项 注意事项 如果使用 volatile 关键字 这段代码在经过优化的编译器 如 Microsoft Visual C 后功能是正常的 优 化的编译器确定 while 循环中的代码不会修改 quit 变量的值 因此 作为优化 编译器可能只使用 quit 变量在 while 循环条件中的初始值 使用 volatile 关键字是告知编译器另一个线程可能会改变 quit 变量的值 这样 编译 器在每次循环运行时都使用更新过后的 quit 变量值 有些时候 当主线程进行其他任务的时候需要暂停辅助线程的运行 如果你暂停正在运行操作系统代码的线程 可能会使得操作系统处于非法状态 因此 在需要暂停的线程中需要始终调用 Windows SDK 的 SuspendThreadfunction 函数 这样 可以确保线程在运行关键代码时不被暂停 在另一个线程中调用 Windows SDK 的 ResumeThreadfunction 是安全的 下面的代码展示了如何使用它们 volatile int quit 0 int main int argc char argv int functionId CmtScheduleThreadPoolFunction DEFAULT THREAD POOL HANDLE ThreadFunction NULL This would typically be done inside a user interface Quit button callback quit 1 CmtWaitForThreadPoolFunctionCompletion DEFAULT THREAD POOL HANDLE functionId 0 return 0 int CVICALLBACK ThreadFunction void functionData while quit return 0 进程和线程优先级进程和线程优先级 在 Windows 操作系统中 可以指定每个进程和线程工作的相对重要性 被称为优先级 如果给予进程或线程以 较高的优先级 那么它们将获得比优先级较低的线程更好的优先选择 这意味着当多个线程需要运行的时候 具有最高优先级的线程首先运行 Windows 将优先级分类 同一进程中的所有线程拥有相同的优先级类别 同一进程中的每个线程都有着与进程 优先级类别相关的优先级 可以调用 Windows SDK 中的 SetProcessPriorityClass 函数来设定系统中线程的优先级 021 65557838 800 820 3622 china info National Instruments NI 公司不推荐用户将线程的优先级设为实时优先级 除非只在很短时间内这样做 当进程被设为实时优先级时 它运行时系统中断会被阻塞 这会造成鼠标 键盘 硬盘及其它至关重要的系统特性不能工作 并很可能造成 系统被锁定 如果你是使用 CmtScheduleThreadFunctionAdv 函数来将函数调度到线程池中运行 那么还可以指定执行所调度函 数的线程的优先级 线程池在运行被调度的函数前会改变线程优先级 在函数结束运行后 线程池会将线程优 先级恢复到原来的优先级 可使用 CmtScheduleThreadFunctionAdv 函数来在默认的和自定义的线程池中指定线程 的优先级 在创建自定义的 LabWindows CVI Utility Library 线程池 调用 CmtNewThreadPool 函数 时 可以设定池中各线程的默认 优先级 消息处理消息处理 每个创建了窗口的线程必须对 Windows 消息进行处理以避免系统锁定 用户界面库中的 RunUserInterfacefunction 函数包含了处理 LabWindows CVI 用户界面事件和 Windows 消息的循环 用户界面库中的 GetUserEvent 和 ProcessSystemEventsfunctions 函数在每次被调用时对 Windows 消息进行处理 如果下列情况中的之一被满足 那 么程序中的每个线程都需要调用 GetUserEventor 和 ProcessSystemEventsregularly 函数来处理 Windows 消息 线程创建了窗口但没有调用 RunUserInterface 函数 线程创建了窗口并调用了 RunUserInterface 函数 但是在返回到 RunUserInterface 循环前需要运行的回调 函数占用了大量时间 多于几百毫秒 但是 在代码中的某些地方不适合用于处理 Windows 消息 在 LabWindows CVI 的用户界面线程中调用了 GetUserEvent ProcessSystemEvents 或 RunUserInterface 函数时 线程可以调用一个用户界面回调函数 如果在用 户界面回调函数中调用这些函数之一 那么线程将调用另外一个回调函数 除非需要这样做 否则这种事件将 产生不可预知的行为 Utility Library 中的多线程函数会造成线程在循环中等待 允许你指定是否在等待线程中对消息进行处理 例如 CmtWaitForThreadPoolFunctionCompletion函数中有个Option参数 可以使用它来指定处理Windows消息的等待线程 有的时候 线程对窗口的创建不是那么显而易见的 用户界面库函数如 LoadPanel CreatePanel 和 FileSelectPopup 等都创建了用于显示和丢弃的窗口 这些函数还为每个调用它们的线程创建了隐藏的窗口 在销毁可见的窗口 时 这个隐藏的窗口并没有被销毁 除了这些用户界面库函数外 各种其它的 LabWindows CVI 库函数和 Windows API 函数创建了隐藏的背景窗口 为了避免系统的锁定 必须在线程中对使用这两种方法创建的窗口的 Windows 消息进行处理 使用线程局部变量使用线程局部变量 线程局部变量与全局变量相似 可以在任意线程中对它们进行访问 但是 全局变量对于所有线程只保存一个 值 而线程局部变量为每个访问的线程保存一个独立的值 当程序中需要同时在多个上下文中进行相同的任务 而其中每个上下文都对应一个独立的线程时 通常需要使用到线程局部变量 例如 你编写了一个并行的测试 021 65557838 800 820 3622 china info National Instruments 程序 其中的每个线程处理一个待测单元 那么你可能需要使用线程局部变量来保存每个单元的特定信息 例 如序列号 Windows API 提供了用于创建和访问线程局部变量的机制 但是该机制对每个进程中可用的线程局部变量的数目 进行了限定 LabWindows CVI Utility Library 中的线程局部变量函数没有这种限制 下面的代码展示了如何创建和 访问一个保存了整数的线程局部变量 volatile int quit 0 volatile int suspend 0 int main int argc char argv int functionId HANDLE threadHandle CmtScheduleThreadPoolFunction DEFAULT THREAD POOL HANDLE ThreadFunction NULL This would typically be done in response to user input or a change in program state suspend 1 CmtGetThreadPoolFunctionAttribute DEFAULT THREAD POOL HANDLE functionId ATTR TP FUNCTION THREAD HANDLE ResumeThread threadHandle return 0 int CVICALLBACK ThreadFunction void functionData while quit if suspend SuspendThread GetCurrentThread suspend 0 return 0 int CVICALLBACK ThreadFunction void functionData int tlvHandle int gSecondaryThreadTlvVal int main int argc char argv 021 65557838 800 820 3622 china info National Instruments int functionId int tlvPtr if InitCVIRTE 0 argv 0 0 return 1 out of memory CmtNewThreadLocalVar sizeof int NULL NULL NULL CmtScheduleThreadPoolFunction DEFAULT THREAD POOL HANDLE ThreadFunction 0 CmtWaitForThreadPoolFunctionCompletion DEFAULT THREAD POOL HANDLE functionId 0 CmtGetThreadLocalVar tlvHandle tlvPtr Assert that tlvPtr has been incremented only once in this thread assert tlvPtr gSecondaryThreadTlvVal CmtDiscardThreadLocalVar tlvHandle return 0 int CVICALLBACK ThreadFunction void functionData int tlvPtr CmtGetThreadLocalVar tlvHandle tlvPtr gSecondaryThreadTlvVal tlvPtr return 0 在线程局部变量中存储动态分配的数据在线程局部变量中存储动态分配的数据 如果你使用线程局部变量来存储动态分配到的资源 那么你需要释放掉分配的资源的每一个拷贝 也就是说 你需要释放掉每个线程中分配到的资源拷贝 使用 LabWindows CVI 的线程局部变量 你可以指定用于销毁线程 局部变量的回调函数 当你销毁线程局部变量时 每个访问过变量的线程都会调用指定的回调函数 下面的代 码展示了如何创建和访问保存了动态分配的字符串的线程局部变量 int CVICALLBACK ThreadFunction void functionData void CVICALLBACK StringCreate char strToCreate void CVICALLBACK StringDiscard void threadLocalPtr int event void callbackData unsigned int threadID int tlvHandle volatile int quit 0 volatile int secondStrCreated 0 int main int argc char argv int functionId 021 65557838 800 820 3622 china info National Instruments if InitCVIRTE 0 argv 0 0 return 1 out of memory CmtNewThreadLocalVar sizeof char NULL StringDiscard NULL CmtScheduleThreadPoolFunction DEFAULT THREAD POOL HANDLE ThreadFunction Secondary Thread StringCreate Main Thread while secondStrCreated ProcessSystemEvents Delay 0 001 CmtDiscardThreadLocalVar tlvHandle quit 1 CmtWaitForThreadPoolFunctionCompletion DEFAULT THREAD POOL HANDLE functionId 0 return 0 int CVICALLBACK ThreadFunction void functionData char sString Create thread local string variable StringCreate char functionData Get thread local string and print it CmtGetThreadLocalVar tlvHandle printf Thread local string s n sString secondStrCreated 1 while quit ProcessSystemEvents Delay 0 001 return 0 void CVICALLBACK StringCreate char strToCreate char tlvStringPtr CmtGetThreadLocalVar tlvHandle tlvStringPtr malloc strlen strToCreate 1 strcpy tlvStringPtr strToCreate void CVICALLBACK StringDiscard void threadLocalPtr int event void callbackData unsigned int threadID char str char threadLocalPtr 021 65557838 800 820 3622 china info National Instruments free str 一些分配的资源必须在分配到它们的线程中释放 这些资源被称为拥有线程关联度 例如 面板必须在创建它 的线程中销毁掉 在调用 CmtDiscardThreadLocalVar 时 Utility Library 在线程中调用被称为 CmtDiscardThreadLocalVar 的线程局部变量销毁回调函数 Utility Library 为每一个访问过该变量的线程调用一次销毁回调函数 它将 threadID 参数传递给销毁回调函数 这个参数指定了调用销毁回调函数的线程的 ID 号 你可以使用这个线程 ID 来确定 是否可以直接释放掉拥有线程关联度的资源还是必须在正确的线程中调用 Toolslib 中的 PostDeferredCallToThreadAndWait 函数来释放资源 下面的代码显示了如何更改前面的例子以在分配字符串的线程 中将它们释放掉
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 湖南省湘潭市雨湖区2024-2025学年四年级下学期期末考试语文试题(无答案)
- 江苏省南京市29中学2026届英语九年级第一学期期末预测试题含解析
- 2026届江苏省南京市临江高级中学高三上学期一模物理试题(无答案)
- 2026届内蒙古自治区通辽市化学九上期中调研模拟试题含解析
- 2026届辽宁省大连市名校英语九年级第一学期期末检测试题含解析
- 广西玉林市北流市2026届化学九上期中监测试题含解析
- 北京海淀人大附2026届九上化学期中考试试题含解析
- 做个有缘人第9课【老师您好】 课件2025-2026学年北师大版(2015)初中心理健康七年级全一册
- 2026届北京顺义化学九上期中检测试题含解析
- 商铺租赁合同签订中的租赁期限与续约规定
- 2025-2026学年浙教版小学劳动技术一年级上册教学计划及进度表
- 本科教学合格评估汇报
- 挖机线路改造方案(3篇)
- 2025年江苏无锡学院招聘高层次人才(长期)笔试模拟试题及参考答案详解一套
- 心电图监护中患者护理查房
- 胃肠间质瘤诊疗指南2025年版
- 耳石症的诊断与治疗
- 2025年民政行业技能鉴定考试-殡仪服务员考试历年参考题库含答案解析(5套共100道单选题合辑)
- 医务人员职业道德与服务礼仪培训
- 煤炭洗选技术课件教学
- 《西门子触摸屏组态与应用》课件
评论
0/150
提交评论