Как управлять многими процессорами, интенсивно использующими процессор, не замедляя работу интерфейса? - PullRequest
0 голосов
/ 11 апреля 2020

Мое приложение Xamarin.Forms содержит Slider, который при изменении вызывает обработку изображения на основе значения Slider. Обработка изображения - это интенсивная загрузка процессора, которая занимает около 200 мс. В результате чувствительность ползунка очень низкая.

Я переместил обработку изображения в отдельный поток, но отклик все еще медленный. Однако, когда я заменяю операцию на Thread.Sleep(200), отклик велик. Это заставляет меня поверить, что даже при запуске в отдельном потоке обработка изображений занимает все ресурсы ЦП, поскольку значение Slider может меняться гораздо чаще.

Вот мое текущее решение: когда Slider изменяется, добавьте обработку изображений Action в список (но пока не выполняйте ее). Каждые 1000 мс берите последний Action в списке и очищайте список. Выполните Action в асинхронном режиме. Это предотвратило бы слишком много задач обработки изображений, занимающих все процессорное время. Вот упрощенная версия моего кода:

public class MainViewModel : ViewModelBase
{
    private Timer timer;
    private List<Action> actions;

    public MainViewModel()
    {
        timer = new Timer(200);
        timer.Elapsed += TimerElapsed;
        timer.AutoReset = true;
        timer.Enabled = true;

        actions = new List<Action>();
    }

    // Processed image to be displayed
    private SKBitmap image;
    public SKBitmap Image
    {
        get => image;
        set => Set(ref image, value);
    }

    // Slider value to be used in image processing
    private double threshold;
    public double Threshold
    {
        get => threshold;
        set => Set(ref threshold, value);
    }

    // Command is called whenever the Slider value is changed
    public ICommand SetThresholdCommand =>
        new RelayCommand(() =>
        {
            actions.Add(() => ProcessImageAsync(Threshold));
        });

    private async void ProcessImageAsync(double threshold)
    {
        SKBitmap processedImage = null;

        await Task.Run(() =>
        {
            processedImage = VisionService.ProcessImage(threshold);
        }

        Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>
        {
            Image = processedImage;
        }
    }

    private void TimerElapsed(object sender, ElapsedEventArgs e)
    {
        if (actions.Any())
        {
            actions.Last()();  // execute the last action added
            actions.Clear();   // ignore all previous actions
        }
    }
}

Это решение работает, но наличие таймера кажется хакерским, и мне не нравится произвольное значение 1000 мс, которое не является оптимальным и, скорее всего, потребуется быть другим на других устройствах. Какие есть лучшие решения?

...