翻译自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 |