翻译自Halcon/C#/Multithreading,不过,界面换成了WPF格式,外形还是一样的。
多线程中需要保证数据共享在任何时候都是有效的,这就需要用到Mutex;同时Event用于线程间的同步;
主界面代表主线程,同时,也是现实结果的地方;
当我们点击‘Start’按钮时,线程threadAcq和threadIP分别被触发,进行图像采集和图像处理;
当点击‘Stop’按钮时,StopEvent将被发送出去,导致线程结束当前的操作,关闭句柄,离开线程函数;
数据共享的部分为:
线程 | 对象 | 类型 |
threadAcq & threadIP | image | ArraryList imgList |
threadIP & main thread | results | struct ResultContainer resultData |
会用Mutex的地方:
数据名称 | 互斥器名称 |
imgList | newImageMutex |
resultData | resultDataMutex |
信号交换:
线程 1 | 方向 | 线程 2 | 信号 |
threadAcq | -- > | threadIP | newImageEvent |
threadIP | -- > | main thread | newResultEvent |
threadIP | < -- | main thread | containerIsFreeEvent |
当然,所有的线程会监听StopEvent信号。
1)主界面分两行
<Grid.RowDefinitions> <RowDefinition/> <RowDefinition Height="60"/> </Grid.RowDefinitions>
2)第一行(0,0)又分为一行两列
<Grid Grid.Row="0" Grid.Column="0"> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition Width="180"/> </Grid.ColumnDefinitions> <StackPanel Grid.Row="0" Grid.Column="0" VerticalAlignment="Center"> <ip:HSmartWindowControlWPF Name="hWindow" Width="384" Height="259"/> </StackPanel> <StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center"> <Button Name="btnStart" Content="Start" Click="btnStart_Click" Width="100" Height="40" Margin="10,10,10,10"/> <Button Name="btnStop" Content="Stop" Click="btnStop_Click" Width="100" Height="40" Margin="10,10,10,10"/> </StackPanel> </Grid>
上面包括一个Halcon窗体,和右边的两个按钮;
3)第二行(1,0)又分为两行两列
<Grid Grid.Row="1" Grid.Column="0"> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="135"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <StackPanel Grid.Row="0" Grid.Column="0"> <Label Name="lbl1" Content="Processing Time: " HorizontalAlignment="Left"/> </StackPanel> <StackPanel Grid.Row="0" Grid.Column="1"> <Label Name="lblProcessTime" Content="" HorizontalAlignment="Left"/> </StackPanel> <StackPanel Grid.Row="1" Grid.Column="0"> <Label Name="lbl2" Content="Data Code Content: " HorizontalAlignment="Left"/> </StackPanel> <StackPanel Grid.Row="1" Grid.Column="1"> <Label Name="lblData" Content="" HorizontalAlignment="Left"/> </StackPanel> </Grid>
用于显示结果;
4)界面显示:
5)图像采集部分:异步读取图像;等待newImgMutex;添加图像到列表(知道读满);释放newImageMutex;设定信号newImgEvent为On(知会图像处理线程);检查Stop信号,如有,则立即退出;
public void ImgAcqRun() { // ------------------- INIT ---------------- string sequenceName = "datacode/ecc200/ecc200.seq"; HFramegrabber acquisition = new HFramegrabber("File", 1, 1, 0, 0, 0, 0, "default", -1, "default", -1, "default", sequenceName, "default", -1, -1); // ----------- WAIT FOR EVENTS --------------- while (true) { HImage grabbedImage = acquisition.GrabImageAsync(-1); newImgMutex.WaitOne(); // CriticalSect if (imgList.Count < MAX_BUFFERS) { imgList.Add(grabbedImage); } else { grabbedImage.Dispose(); } newImgMutex.ReleaseMutex(); // CriticalSect newImgEvent.Set(); if (delegatedStopEvent.WaitOne(0, true)) break; } // -------- RESET/CLOSE ALL HANDLES --------- acquisition.Dispose(); newImgEvent.Reset(); return; }
6)图像处理部分:等待图像互斥器newImgMutex;取列表中的第一张图像并移除;释放互斥器;读取二维码;等待信号containerIsFreeEvent(也就是上一组数据是否已显示完毕);填写数据到Container;释放互斥器;发送信号newResultEvent(通知主线程可以更新数据了);也是要时刻监控Stop信号。
public void IPRun() { // ------------------- INIT ---------------- HDataCode2D reader = new HDataCode2D("Data Matrix ECC 200", new HTuple(), new HTuple()); reader.SetDataCode2dParam("default_parameters", "enhanced_recognition"); // ----------- WAIT FOR EVENTS --------------- while (newImgEvent.WaitOne()) { newImgMutex.WaitOne(); // CriticalSect HImage image = (HImage)imgList[0]; // CriticalSect imgList.Remove(image); // CriticalSect newImgMutex.ReleaseMutex(); // CriticalSect HTuple t1 = HSystem.CountSeconds(); HTuple decodedDataStrings, resultHandle; HXLD symbolXLDs = reader.FindDataCode2d(image, new HTuple(), new HTuple(), out resultHandle, out decodedDataStrings); HTuple t2 = HSystem.CountSeconds(); containerIsFreeEvent.WaitOne(); resultDataMutex.WaitOne(); // CriticalSect resultData.timeNeeded = (1000 * (t2 - t1)); // CriticalSect resultData.decodedData = decodedDataStrings; // CriticalSect resultData.resultImg = image; // CriticalSect resultData.resultHandle = resultHandle; // CriticalSect resultData.symbolData = symbolXLDs; // CriticalSect containerIsFreeEvent.Reset(); // CriticalSect resultDataMutex.ReleaseMutex(); ; // CriticalSect newResultEvent.Set(); //mainForm.Invoke(delegateDisplay); mainForm.Dispatcher.Invoke(delegateDisplay); if (delegatedStopEvent.WaitOne(0, true)) break; } // -------- RESET/CLOSE ALL HANDLES --------- mainForm.threadAcq.Join(); //mainForm.Invoke(delegateControlReset); mainForm.Dispatcher.Invoke(delegateControlReset); reader.Dispose(); newResultEvent.Reset(); return; }
注意到在WPF中Invoke被封装到Dispatcher中,所以委托的方式稍有不同。
7)运行界面
详细的源代码,见(Halcon)Multithreading。
线程 2 |