Дротик будущего блокирует основной поток

#flutter #dart #future

#флаттер #дротик #будущее

Вопрос:

Я работаю над приложением, которое захватывает и обрабатывает изображение. Упрощенная версия кода:

 build() {
   return FloatingActionButton(
        onPressed: processImage,
        child: Icon(
          Icons.camera_alt,
          color: color,
        ),
      ); 
}

processImage(Camera image) async {
   await image.process();   
}
  

И в другом классе:

 Future<image> process() {
  return Future(() {
    for (int x = 0; x < width; x  ) {
      for (int y = 0; y < height; y  ) {
        //process image
      }
    }
  });
}
  

Но при process() запуске пользовательский интерфейс зависает.

Почему это происходит? Разве эта функция не передается будущему конструктору, работающему в фоновом режиме?

Комментарии:

1. Я не уверен на 100%, но я знаю, что dart использует цикл событий, и все будет выполняться в основном потоке (возможно, в какой-то момент в будущем, в случае асинхронного кода). Таким образом, этот код в конечном итоге будет выполнен в основном потоке, вызывая блокировку. Рассмотрите возможность использования изолятов Dart; это должно устранить проблему. У Flutter есть встроенный удобный метод, позволяющий сделать это за вас, но я не могу вспомнить, как он называется в моей голове.

2. Я полагаю, что нашел это: api.flutter.dev/flutter/foundation/compute.html

3. @GregoryConrad Спасибо, это сработало для меня, это не очень элегантное решение, но оно работает, если вы хотите сделать это ответом, я буду рад сделать это правильным ответом

4. что не является «элегантным» в compute() функции?

5. @pskink Моя функция получает 6 параметров, и вы не можете вызвать метод класса: "The callback argument must be a top-level function, not a closure or an instance or static method of a class" , и вы можете передать только один параметр, поэтому мне пришлось преобразовать все мои параметры в a Map<String, dynamic> , создать функцию верхнего уровня, которая получает карту, и деструктировать исходные переменные.

Ответ №1:

Поскольку Dart использует цикл событий, весь код (синхронный и асинхронный) будет просто выполняться на одном и том же isolate (думаю thread , на других языках в качестве аналогии), только в разные моменты времени. Таким образом, когда ваш process метод выводится из очереди и выполняется, он блокирует поток и приводит к удалению кадров из-за более длительного времени выполнения, несмотря на то, что он асинхронный. Оптимальное решение проблемы — создать другой изолированный поток в новом потоке и выполнить вычисления там. Flutter предоставляет удобный метод для этого конкретного варианта использования, называемый compute . Он принимает функцию верхнего уровня (не в классе и не анонимную), которая может иметь параметр примитивного типа (включая Map и List ) в качестве аргумента и вернется в какой-то момент в будущем. Для получения дополнительной информации compute см. Его Документацию, указанную выше.

Если у вас есть несколько параметров, которые вам нужно передать compute , общим шаблоном (за пределами только этого варианта использования) является создание метода, который сериализует поля класса в a Map<String, dynamic> , и фабричного конструктора, который создает объект из a Map<String, dynamic> . Этот процесс был бы проще с отражением, но Flutter отключает его по соображениям производительности.

Полный пример compute из документации Flutter смотрите Здесь: https://flutter.dev/docs/cookbook/networking/background-parsing

Ответ №2:

Вы можете вставить пробел в цикл событий.
Простой способ:

 Future<image> process2() {
  return Future(() async {
    for (var x = 0; x < width; x  ) {
      for (var y = 0; y < height; y  ) {        
        // process       
      }

      if (x % 100 == 0) {
        await Future.delayed(Duration(seconds: 100));
      }
    }
  });
}

  

Комментарии:

1. Неправильный ответ, это не поможет. Пользовательский интерфейс по-прежнему зависает.

2. Я обнаружил, что в этой статье говорится об использовании цикла событий для обработки тяжелых задач. Я не знаю, можно ли использовать тот же метод для этого случая (обработка изображения). https://hackernoon.com/executing-heavy-tasks-without-blocking-the-main-thread-on-flutter-6mx31lh