首页 > 解决方案 > 如何提高在两个窗口中完成的 wpf GUI 主线程的性能

问题描述

我有一个 wpf 应用程序,它mainWindow可以创建_otherWindow在辅助监视器上显示的内容。两个窗口都有需要随时间变化的元素。(mainWindow更新一个Imageplots一个图,最后根据一些计算_otherWindow更新一个位置)。shape

我的问题是什么?好吧,我正在 a中逐帧读取视频Thread(但是我想允许使用相机拍摄的流)。当我在特定时间内每帧更新 GUI 时,应用程序的负载越来越大,而且速度越来越慢......

我意识到注释mainWindow updating Image或者注释_otherWindow updating shape position代码可以使应用程序运行良好,但问题是它们何时一起运行。

这里有详细的说明

首先,我计算内部的一些东西_otherWindow并计算 a 的位置shape。然后我计算一些相关image的东西并update frame添加一些东西bitmap 然后我更新内部形状的位置_otherWindow 最后我绘制结果(绘图需要从mainWindow和获取的数据_otherWindow)为此,我使用任务并等待它们。

我有这个:

private Thread _camera;
private void CaptureVideo()
{
    _camera = new Thread(CaptureVideoCallback)
    {
        Priority = ThreadPriority.Highest
    };
    _camera.Start();
}


private VideoCapture _capture;
private void CaptureVideoCallback()
{
    //some computing here read from a video file...
     _capture = new VideoCapture("someVideo.mp4");
    for (var i = 0; i < _capture.FrameCount; i++)
    {
        _capture.Read(_frame);
        if (_frame.Empty()) return;

        //*************task that does heavy computation in other class
        var heavyTaskOutput1 = Task.Factory.StartNew(() =>
            {
                _otherWindow.Dispatcher.Invoke(() =>
                {
                    ResultFromHeavyComputationMethod1 = _otherWindow.HeavyComputationMethod1();
                });
            }
        );
                
        ////*************task that does heavy computation in current class  
        var heavyTaskOutput2 = Task.Factory.StartNew(() =>
        {
            ResultFromHeavyComputationMethod2 = HeavyComputationMethod2(ref _frame);
            var bitmap = getBitmapFromHeavyComputationMethod2();
            bitmap.Freeze();
            //update GUI in main thread
            Dispatcher.CurrentDispatcher.Invoke(() => ImageSource = bitmap);
        });

        ////*************wait both task to complete     
        Task.WaitAll(heavyTaskOutput1, heavyTaskOutput2 );
        
        //update _otherWindow GUI 
        var outputGui = Task.Factory.StartNew(() =>
            {
                _otherWindow.Dispatcher.Invoke(() =>
                {
                    _otherWindow.UpdateGui();
                });
            }
        );
        outputGui.Wait();

        
        ////*************plot in a char using gotten results, UPDATE GUI
        Task.Run(() =>
        {
            PlotHorizontal();
        });    
    }
} 

什么是加快速度的好方法?我的意思是我知道 GUI 的东西需要在主线程上完成,但这会减慢速度。

编辑

按照 Clemens 的建议更改了代码:

//*************task that does heavy computation in other class
var heavyTaskOutput1 = Task.Run(() =>
    {
        ResultFromHeavyComputationMethod1 = _otherWindow.HeavyComputationMethod1();
    }
);
        
////*************task that does heavy computation in current class  
var heavyTaskOutput2 = Task.Run(() =>
{
    ResultFromHeavyComputationMethod2 = HeavyComputationMethod2(ref _frame);
    var bitmap = getBitmapFromHeavyComputationMethod2();
    bitmap.Freeze();
    //update GUI in main thread
    Dispatcher.CurrentDispatcher.Invoke(() => ImageSource = bitmap);
});

////*************wait both task to complete     
Task.WaitAll(heavyTaskOutput1, heavyTaskOutput2);

//update _otherWindow GUI 
var outputGui = Task.Run(() =>
    {
        _otherWindow.Dispatcher.Invoke(() =>
        {
            _otherWindow.UpdateGui();
        });
    }
);
outputGui.Wait();

标签: c#wpfperformancetask

解决方案


有点难以猜测。你有 Visual Studio 吗?我认为即使是社区版也有一些分析功能(菜单:Analyze/Performance Profiler...)。这可能会指出一些不明显的瓶颈。

我的想法:

  1. getBitmapFromHeavyComputationMethod2似乎每次都返回一个新的位图。我无法推断它返回的实际类型,但它可能涉及半大型非托管内存分配和实现IDisposable。您可能会检查您是否适当地处理了它。

  2. 您可以使用WriteableBitmap ,而不是为每一帧创建一个新的位图吗?如果您这样做,请务必锁定和解锁它。如果需要,也许可以在两个位图之间进行 ping-pong(交替)。

  3. 看来您可能正在使用您的 I/O 读取(第一个,然后是另一个)序列化您的“繁重计算”。也许也启动读取async,并在你的等待它,WaitAll以便计算和 I/O 可以同时发生。这种形状的东西:

var readResult = _capture.Read(_frame);
for (...) {
    // check read result
    // ...
    // launch heavy computation

    readResult = Task.Run(() => _capture.Read(nextFrame);
    Task.WaitAll(pupilOutput, outputTest, readResult);
    _frame = nextFrame;
}

请注意,对于N帧,这将读取N+1次——也许您的方法可以接受。Read


推荐阅读