Вопрос:

Как повысить производительность основного потока графического интерфейса wpf, который выполняется в двух окнах

c# wpf performance task

40 просмотра

1 ответ

13353 Репутация автора

У меня есть приложение wpf, оно mainWindowсоздает _otherWindowто, что отображается на дополнительном мониторе. Оба окна имеют элементы, которые должны меняться вместе со временем. ( mainWindowобновляет график, Imageа также plotsграфик, наконец, _otherWindowобновляет shapeпозицию в зависимости от некоторых вычислений).

В чем моя проблема? хорошо, я читаю видео кадр за кадром в 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();
        });    
    }
} 

Что было бы хорошим способом ускорить это? Я имею в виду, что я знаю, что вещи с графическим интерфейсом должны выполняться в основном потоке, но это замедляет работу.

редактировать

Изменили код, как предложил Клеменс:

//*************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();
Автор: cMinor Источник Размещён: 12.06.2019 07:07

Ответы (1)


0 плюса

2297 Репутация автора

Это немного сложно догадаться. У вас есть Visual Studio? Я думаю, что даже редакция Community имеет некоторые возможности профилирования (меню: Анализ / Performance Profiler ...). Это может указывать на некоторые неочевидные узкие места.

Мои мысли:

  1. getBitmapFromHeavyComputationMethod2По-видимому, каждый раз возвращает новый растровое изображение. Я не могу вывести фактический тип, который он возвращает, но он, вероятно, включает в себя небольшое распределение неуправляемой памяти и реализует IDisposable. Вы можете проверить, правильно ли вы это выбрасываете.

  2. Вместо того, чтобы создавать новое растровое изображение для каждого кадра, вы можете использовать WriteableBitmap ? Не забудьте заблокировать и разблокировать его, если вы делаете. Возможно пинг-понг (альтернативный) между двумя растровыми изображениями, если вам нужно.

  3. Похоже, что вы, возможно, сериализуете свои «тяжелые вычисления» со считыванием ввода / вывода (сначала одно, затем другое). Возможно, запустите чтение asyncкак, и подождите его в вашем, WaitAllчтобы вычисления и ввод / вывод могли происходить одновременно. Что-то в этой форме:

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 + 1 раз для N кадров - возможно, ваш Readметод в порядке с этим.

Автор: Curt Nichols Размещён: 12.06.2019 06:23
Вопросы из категории :
32x32