例程介绍
此示例展示了如何实现管道模式,这是一种高级编程概念,在实时数据处理系统中尤为有用。了解并掌握管道模式对于提升程序效率和响应速度至关重要。
* 此示例基于inspect_bottle_mouth.hdev,这表明了代码的可扩展性和模块化设计的重要性。通过复用和扩展现有代码,可以加速开发过程。
* 管道由(多个)生产者/消费者阶段组成,这是并发编程中的一个基本概念。理解并应用生产者/消费者模式有助于设计高效、可扩展的并发系统。
例程代码
*
* 设置处理参数,这是编程中常见的做法,有助于代码的灵活性和可配置性。
MessageQueueMaxMessageNum := 200 // 限制消息队列的大小,有助于防止内存溢出和提高系统稳定性。
NumAOPThreads := 1 // 设置异步操作线程数,这是优化程序性能的关键参数之一。
NumAOPThreadsForImageProcessing := 1 // 专门为图像处理设置线程数,这体现了对特定任务进行资源分配的重要性。 * 估计可能的平均采集间隔,这是性能调优的重要步骤。
estimate_image_processing_time (0.0025, NumAOPThreadsForImageProcessing, EstimatedImageProcessingTime)
MeanAcquisitionInterval := EstimatedImageProcessingTime // 根据处理时间设置采集间隔,确保数据处理的实时性。 set_system ('thread_num', NumAOPThreads) // 设置系统线程数,这是并发编程中的基础配置。 * 初始化部分,体现了良好的编程习惯,如关闭不必要的窗口、设置显示字体等。
get_system ('store_empty_region', StoreEmptyRegion)
set_system ('store_empty_region', 'true')
dev_update_off ()
dev_close_window ()
dev_open_window (0, 0, 640, 480, 'white', WindowHandle)
set_display_font (WindowHandle, 16, 'mono', 'true', 'false') * 创建消息队列,并限制其大小,这是管道模式中的核心部分。
create_message_queue (QueueOriginalImage) // 创建原始图像消息队列。
set_message_queue_param (QueueOriginalImage, 'max_message_num', MessageQueueMaxMessageNum) // 设置消息队列的最大消息数。
create_message_queue (QueueImageProcessingResult) // 创建图像处理结果消息队列。
set_message_queue_param (QueueImageProcessingResult, 'max_message_num', MessageQueueMaxMessageNum) // 同样设置最大消息数。 * 准备用于终止管道的数据,体现了程序的健壮性和错误处理能力。
create_event ('', '', StopAcq) // 创建停止采集事件。
TerminationKey := 'terminate' // 定义管道终止的消息键,这是控制管道流程的关键。 * 启动执行图像采集、图像处理和结果评估的线程,这是并发编程的核心。
par_start<AcquisitionThread> : acquire_images (MeanAcquisitionInterval, StopAcq, TerminationKey, QueueOriginalImage) // 启动图像采集线程。
par_start<ImageProcessingThread> : process_images (QueueOriginalImage, NumAOPThreadsForImageProcessing, TerminationKey, QueueImageProcessingResult) // 启动图像处理线程。
par_start<ResultEvaluationThread> : evaluate_results (QueueImageProcessingResult, TerminationKey, WindowHandle) // 启动结果评估线程。 * 让程序运行一段时间,然后停止采集阶段,从而终止管道。这体现了对程序执行流程的控制能力。
wait_seconds (max([5,720 * MeanAcquisitionInterval * 1.5])) // 等待一段时间,确保程序有足够的时间运行。
signal_event (StopAcq) // 触发停止采集事件,终止采集线程。 * 等待所有线程结束,这是确保程序正确结束的重要步骤。
par_join ([AcquisitionThread,ImageProcessingThread,ResultEvaluationThread]) // 等待所有线程完成。 * 重置系统参数
set_system ('store_empty_region', StoreEmptyRegion)
多线程
在Halcon中,par_start<> 和 par_join 是用于多线程处理的重要工具。以下是对这两个工具以及多线程的简单介绍:
一、Halcon中的多线程处理
-
par_start<>
- 功能:用于启动一个新的子线程来并行执行程序或调用算子。
- 用法:
par_start<ThreadID> : 算子/方法。其中,ThreadID是子线程的标识符,是一个取决于操作系统的整数进程号。par_start不是一个实际的算子,而是一个修改调用行为的限定符,它必须放在要并行执行的算子或方法之前。 - 示例:
par_start<ThreadID>: C_L_U(GrayImage, Cross, ...),这表示启动一个新的子线程来执行C_L_U算子。
-
par_join
- 功能:用于等待由
par_start启动的一个或多个子线程完成。 - 用法:
par_join(ThreadID)或par_join([ThreadID1, ThreadID2, ...])。其中,ThreadID(或ThreadID1,ThreadID2, ...)是要等待的子线程的标识符。 - 示例:
par_join([ThreadID, ThreadID1, ThreadID2, ...]),这表示等待所有指定的子线程完成。
- 功能:用于等待由
二、多线程的简单介绍
-
定义:多线程是指从软件或者硬件上实现多个线程并发执行的技术。线程是进程中的一部分,是进程的实际运作单位,也是操作系统中的最小运算调度单位。
-
原理:在一个程序中,这些独立运行的程序片段叫作“线程”。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。
-
优点:
- 提高CPU的使用率。
- 提高应用程序的响应速度和效率,尤其对于图形界面的程序来说,可以避免在耗时长的操作时整个系统无响应的情况。
- 使多CPU系统更加有效,操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。
-
缺点:
- 如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换。
- 更多的线程需要更多的内存空间。
- 线程可能会给程序带来更多“bug”,因此需要小心使用。
- 线程的中止需要考虑其对程序运行的影响。
- 需要防止线程死锁情况的发生。
在Halcon中,通过合理使用par_start<>和par_join,可以高效地实现多线程处理,提高程序的执行效率和响应速度。但同时也要注意多线程可能带来的问题,如资源竞争、死锁等,需要合理设计和控制线程的数量和行为。
