从辅助线程更新控制:是调用性能关键

本文关键字:调用 性能 控制 线程 更新 | 更新日期: 2023-09-27 17:50:15

我正在开发的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)你认为使用同步调用或异步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线程使它的副本,它真的是不安全的依赖于异步机制?

从辅助线程更新控制:是调用性能关键

调用Invoke是昂贵的,只是因为它涉及到线程上下文切换和(可能)等待UI线程可用。但如果你的UI线程没有被其他事情缠住,你也没有试图在一秒钟内进行数百次picturebox更新,那么这就不太可能是一个明显的问题。如果图像的大小很大,那么绘制像素将花费很多时间,因此Invoke所花费的微秒将是微不足道的。

更重要的是,必须在UI线程上执行更新。所以要么你在UI线程上做所有的处理,要么你使用Invoke来封送UI线程的更新。因为你不想占用UI线程做计算,所以你真的没有选择。

img作为函数参数或作为类的成员的区别是不显著的。

BeginInvoke可能是有用的,但你必须小心。所有BeginInvoke所做的就是将请求排队,以便后台线程可以继续工作,UI线程可以在有时间时更新其显示。使用BeginInvoke很容易将如此多的请求排队,以至于UI线程几乎花费了所有的时间来处理这些请求,并且UI的其余部分似乎被锁定,因为用户发起的操作在更新操作之后被塞进队列。如果你每秒钟做很多次图像更新,当然这取决于图像的大小,你的UI线程永远赶不上。你的UI将被锁定,最终你将耗尽内存来排队请求。

看来您的主要性能瓶颈是对图像进行计算,然后显示图像。这两个操作都是非常耗时的,所以无论你花在Invoke上的时间是什么,都可能是无关紧要的。

如果你的程序使用Invoke的性能足够好,那么你可能最好让它保持原样。您可以尝试使用BeginInvoke,但正如您所说,这将需要克隆图像。此外,您可能会遇到一个锁定的UI。

简而言之,你的问题的答案是:"看情况而定。"我不太了解你的图像处理,也不知道做这个处理需要多少时间。如果你很好奇,可以用BeginInvoke试试。最糟糕的情况是程序崩溃,您必须重新使用Invoke。但一定要在较长时间内彻底测试。

你最后一个问题不清楚。如果你问在后台线程上做UI更新是否危险,答案是响亮的"是的!"在UI线程之外的任何线程上更新UI都可能导致各种奇怪的bug,这些bug有时几乎不可能复制,而且很难追踪。Windows期望UI元素在单个线程中更新。