我正在开发的c#应用程序主要包括对来自相机的图像执行操作并将其打印在图片框上。 它有一个用c ++编写的库,它从网络摄像头(imgcam)中检索图像,然后复制它们(imgcam_copy),然后将它们传送给请求它们的托管代码。 反过来,应用程序的托管部分包含一个辅助线程,该辅助线程执行一个while循环,从非托管库中获取图像并将它们打印到图片框。
一切正常,一切都是最精致的部分,即从非托管代码到托管代码的动态资源管理对我来说似乎很清楚,我知道我在做什么(至少我希望如此),但是问题在于编写使线程正常工作所需的代码。 事实上,即使在网上浏览数小时之后,我仍有很多事情不清楚。
我不得不用UI线程打印图像,所以我必须依赖Invoke。
delegate void setImageCallback(Image img);
private void showFrame(Image img)
{
if (pboxCam.InvokeRequired)
{
this.Invoke(new setImageCallback(showFrame), img);
}
else
{
pboxCam.Image = img;
}
}
我有很多问题:
1)是否需要进行昂贵的操作?我已经做了很多努力来减少主要操作在图像上的执行时间,在显示结果时浪费部分收益会令人失望。
2)您是否认为最好同时使用Invoke或异步BeginInvoke +图像副本?
3)将Image作为函数参数但是作为类的成员访问它会不会有帮助?
private delegate void setImageCallback();
private void showFrame()
{
if (pboxCam.InvokeRequired)
{
this.Invoke(new setImageCallback(showFrame));
}
else
{
pboxCam.Image = cm.bitmap;
}
}
4)也许你不会分享我对性能的担心,但我想知道你是否分享了线程安全性。 UI线程只是想在非UI线程复制它时显示图像,依赖异步机制真的不安全吗?
答案 0 :(得分:5)
调用Invoke
的代价很高,因为它涉及线程上下文切换并且(可能)等待UI线程变为可用。但是,如果您的UI线程并没有做其他一些事情,并且您不会尝试每秒进行数百个图片框更新,那么它不太可能是一个明显的问题。如果图像具有任何显着的大小,绘制像素将花费很多时间,以至于Invoke
所需的微秒将是无关紧要的。
更重要的是,您必须在UI线程上执行更新。因此,要么在UI线程上执行所有处理,要么使用Invoke
来编组UI线程的更新。由于您不想将UI线程用于计算,因此您无法做出选择。
将img
作为函数参数或作为类的成员之间的区别是无关紧要的。
BeginInvoke
可能很有用,但你必须要小心。所有BeginInvoke
都会对请求进行排队,以便后台线程可以继续工作,UI线程可以在有时间时更新其显示。使用BeginInvoke
排序这么多的请求非常容易,UI线程在处理这些请求的所有时间内花费了很多时间,而UI的其余部分似乎已锁定,因为用户启动的操作被填入更新操作后的队列。如果您每秒进行许多图像更新,当然取决于图像大小,您的UI线程将永远不会赶上。您的UI将锁定,最终您将耗尽内存以排队请求。
这里的主要性能瓶颈似乎是在图像上进行计算然后显示图像。这两项操作都是如此耗费时间,以至于您在Invoke
中花费的时间可能无关紧要。
如果你的节目使用Invoke
的效果足够好,那么你可能最好不要让它保持原样。您可以尝试使用BeginInvoke
但正如您所说的那样,需要克隆图像。此外,您可能会遇到锁定的用户界面。
简而言之,您的问题的答案是,"它取决于。"我不太了解您的图像处理或执行该处理所需的时间。如果您有点好奇,请使用BeginInvoke
进行尝试,然后查看。可能发生的最糟糕的是你的程序崩溃了,你必须回到使用Invoke
。但一定要经过长时间的彻底测试。
您的上一个问题尚不清楚。如果您要问在后台线程上进行UI更新是否有危险,那么答案就是响亮的"是的!"在UI线程以外的任何线程上更新UI可能导致各种奇怪和奇妙的错误,这些错误几乎不可能有时复制,并且很难追踪。 Windows期望在单个线程上更新UI元素。