【问题标题】:How to improve performance on wpf GUI main thread that is done in two windows如何提高在两个窗口中完成的 wpf GUI 主线程的性能
【发布时间】:2019-06-12 07:07:05
【问题描述】:

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

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

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

这里有详细的说明

首先我计算_otherWindow 内部的一些东西并计算shape 的位置。 然后我计算一些与imageupdate 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();

【问题讨论】:

  • heavyTaskOutput1 在它只做Dispatcher.Invoke 时似乎毫无意义。也许在Invoke 之前调用_otherWindow.HeavyComputationMethod1。您可能还想使用Task.Run 而不是Task.Factory.StartNew。参见例如这里:stackoverflow.com/questions/38423472/…
  • 好的,我已经添加了建议的更改。有没有进一步改进的可能?

标签: c# wpf performance task


【解决方案1】:

这有点难猜。你有 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+1N 帧——可能是您的 Read 方法没关系。

【讨论】:

  • 实际上对于writeableBitmap,我对_frame 进行了一些更改(例如在某些roi 上添加圆圈和其他形状)然后:var bitmap = _frame.ToWriteableBitmap(PixelFormats.Bgr24); bitmap.Freeze(); 这是因为_frame 需要转换所以我可以在wpf Image 中显示它,例如:&lt;Image Source="{Binding Path=ImageSource,Mode=TwoWay}"/&gt; 关于阅读的建议会很好,我看到的唯一问题是如果在实时流中,我需要阅读,然后再次阅读并显示图像?
  • 因为我使用 Opencvsharp 来捕捉视频和操作帧,这里是转换器github.com/shimat/opencvsharp/blob/master/src/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多