Предисловие 1: в ближайшее время я обновлю несколько серий текстовых руководств по Flutter.
Ход обновления:Не менее двух статей в неделю;
Место обновления:Сначала он был опубликован на официальном аккаунте, а на следующий день обновился на Наггетс, Сифу и других площадках;
Больше обменов:Вы можете добавить мой WeChat 372623326, подписаться на мой Weibo: coderwhy
Я надеюсь, ты сможешьПомогите вперед, нажмите, чтобы посмотреть, дайте мне больше творческой мотивации.
Предисловие 2. Прежде чем писать эту статью, я не решался объяснять здесь связанные с Dart темы, связанные с асинхронностью, потому что эта часть контента может быть непосильной для новичков:
1. Отношения между одним потоком и асинхронностью более запутанные, хотя я постараюсь сделать все возможное, чтобы вы поняли по-своему.
2. Большое количество асинхронных методов работы (Future, await, async и т.д.), на данный момент не видно конкретных сценариев применения. (Например, если вы изучили промис, ожидание и асинхронность во внешнем интерфейсе, это может быть относительно просто, но я предполагаю, что у вас нет такой основы).
Однако послушайте меня: если у вас все еще есть много сомнений после того, как вы закончите эту главу, это не имеет значения.Когда вы будете использовать соответствующие знания позже, вы будете просветлены, когда будете оглядываться назад.
1. Асинхронная модель Dart
Давайте сначала разберемся, как Dart обрабатывает асинхронные операции.
1.1 Dart является однопоточным
1.1.1.Длительные операции в программе
Трудоемкие операции в разработке:
- В разработке мы часто сталкиваемся с некоторыми трудоемкими операциями, которые необходимо выполнить, такими как сетевые запросы, чтение файлов и т. д.;
- Если наш основной поток ждал завершения этих трудоемких операций, он заблокируется и не сможет реагировать на другие события, такие как клики пользователя;
- Очевидно, мы не можем этого сделать! !
Как справиться с трудоемкими операциями?
- Разные языки имеют разные способы обработки трудоемких операций.
- Первый способ обработки:Многопоточность, такая как Java и C++, нашей обычной практикой является запуск нового потока (Thread), выполнение этих асинхронных операций в новом потоке, а затем передача полученных данных в основной поток через межпотоковое взаимодействие.
- Способ обработки второй:Один поток + цикл обработки событий, такие как JavaScript и Dart, основаны на использовании одного потока и цикла обработки событий для выполнения трудоемких операций. Но как один поток может выполнять трудоемкие операции? !
1.1.2 Однопоточные асинхронные операции
Я сталкивался со многими разработчиками, у которых полно вопросительных знаков по поводу однопоточных асинхронных операций? ? ?
На самом деле они не конфликтуют:
- Поскольку одно из наших приложений большую часть времени находится в состоянии простоя, его взаимодействие с пользователем не ограничено.
- Например, ожидание щелчков пользователя, возврат данных сетевого запроса и операции ввода-вывода при чтении и записи файлов — эти действия ожидания не будут блокировать наши потоки;
- Это связано с тем, что, подобно IO сетевых запросов и чтения и записи файлов, мы можем основываться на неблокирующих вызовах;
Блокирующие и неблокирующие вызовы
Чтобы понять это, нам нужно знать阻塞式调用
и非阻塞式调用
Концепция чего-либо.
- Блокировка и неблокировка относятся кСостояние программы в ожидании результата вызова (сообщение, возвращаемое значение).
- Блокировка вызова:Прежде чем будет возвращен результат вызова, текущий поток будет приостановлен, а вызывающий поток продолжит выполнение только после получения результата вызова.
- Неблокирующий вызов:После выполнения вызова текущий поток не прекратит выполнение, и потребуется лишь некоторое время, чтобы проверить, есть ли возвращенный результат.
Давайте смоделируем на реальном примере:
- Вы голодны в полдень и вам нужно заказать еду на вынос, заказать
外卖的动作
наш призыв, получить最后点的外卖
Вот чего мы ждем. - Блокировка вызова:Закажите еду на вынос, перестаньте что-либо делать, просто подождите глупо, и ваш тред перестанет выполнять любую другую работу.
- Неблокирующий вызов:Закажи еду на вынос и продолжай заниматься другими делами: продолжай работать, играть в игры, твой поток не продолжает выполнять другие дела, просто изредка проверяй, не постучали ли в дверь и доставили ли еду на вынос.
И многие трудоемкие операции в нашей разработке могут основываться на таких非阻塞式调用
:
- Например, сам сетевой запрос использует соединение Socket, а сам Socket предоставляет модель выбора, которую можно использовать для
非阻塞方式的工作
; - Например, операция ввода-вывода для чтения и записи файла, мы можем использовать операцию ввода-вывода, предоставляемую операционной системой.
基于事件的回调机制
;
Ни одна из этих операций не заблокирует дальнейшее выполнение нашего единственного потока, наш поток может продолжать заниматься другими делами в процессе ожидания: выпить чашку кофе, поиграть в игру, а затем перейти к соответствующей обработке, когда будет реальный ответ , может.
На этом этапе у нас может возникнуть два вопроса:
- Вопрос один:Если в многоядерном процессоре один поток недостаточно использует процессор? Я объясню этот вопрос позже.
- Вопрос второй:Как один поток обрабатывает сетевое взаимодействие, операции ввода-вывода и возвращаемые ими результаты? Ответ — цикл событий.
1.2. Цикл событий Dart
1.2.1 Что такое цикл событий
Однопоточная модель в основном предназначена для поддержания цикла событий (Event Loop).
Что такое цикл событий?
- На самом деле цикл событий не сложен, он заключается в том, чтобы поместить ряд событий (включая события кликов, события ввода-вывода, сетевые события), которые необходимо обработать, в очередь событий (Event Queue).
- Непрерывно берите события из очереди событий (Event Queue) и выполняйте соответствующие блоки кода, которые необходимо выполнить, пока очередь событий не опустеет.
Напишем псевдокод для цикла событий:
// 这里我使用数组模拟队列, 先进先出的原则
List eventQueue = [];
var event;
// 事件循环从启动的一刻,永远在执行
while (true) {
if (eventQueue.length > 0) {
// 取出一个事件
event = eventQueue.removeAt(0);
// 执行该事件
event();
}
}
Когда у нас есть некоторые события, такие как события кликов, события ввода-вывода, сетевые события, они добавляются вeventLoop
, когда обнаружится, что очередь событий не пуста, событие будет извлечено и выполнено.
- Cog — это наш цикл обработки событий, который извлекает события из очереди по одному для выполнения.
1.2.2 Моделирование кода цикла событий
Здесь мы рассмотрим псевдокод, чтобы понять, как выполняются события щелчка и события сетевого запроса:
- Это фрагмент кода Flutter, вы можете не понимать многих вещей, но если вы терпеливо прочитаете его, вы поймете, что мы делаем.
- Кнопка RaisedButton, которая выполняет функцию onPressed при нажатии.
- В функции onPressed мы отправляем сетевой запрос, а функция обратного вызова затем будет выполнена после успешного запроса.
RaisedButton(
child: Text('Click me'),
onPressed: () {
final myFuture = http.get('https://example.com');
myFuture.then((response) {
if (response.statusCode == 200) {
print('Success!');
}
});
},
)
Как этот код выполняется в цикле событий?
- 1. Когда пользователь щелкает, функция обратного вызова onPressed помещается в цикл событий для выполнения, и во время выполнения отправляется сетевой запрос.
- 2. После отправки сетевого запроса цикл событий не будет заблокирован, но он будет сброшен, когда обнаружится, что выполняемая функция onPressed завершилась.
- 3. После успешного сетевого запроса будет выполнена переданная функция обратного вызова, которая также является событием.Событие помещается в цикл событий для выполнения.После завершения выполнения цикл событий отбрасывает его.
Хотя обратные вызовы в onPressed и then имеют некоторые отличия, они оба сообщают об этом циклу обработки событий:У меня есть кусок кода, который нужно выполнить, пожалуйста, помогите мне.
2. Асинхронная работа Dart
Асинхронные операции в Dart в основном используют Future, async и await.
Если у вас есть предыдущий опыт программирования ES6 и ES7 во внешнем интерфейсе, вы можете полностью понять Future as Promise, а async, await и ES7 в основном одинаковы.
Но если у вас нет опыта фронтенд-разработки, как вы понимаете Future, async и await?
2.1 Знакомство с будущим
Я долго думал об этом, как объяснить это Будущее?
2.1.1 Синхронизированные сетевые запросы
Сначала рассмотрим пример:
- В этом примере я использую getNetworkData для имитации сетевого запроса;
- Сетевой запрос занимает 3 секунды, после чего данные возвращаются;
import "dart:io";
main(List<String> args) {
print("main function start");
print(getNetworkData());
print("main function end");
}
String getNetworkData() {
sleep(Duration(seconds: 3));
return "network data";
}
Каков результат работы этого кода?
- getNetworkData заблокирует выполнение основной функции
main function start
// 等待3秒
network data
main function end
Очевидно, что приведенный выше код не является тем эффектом выполнения, которого мы хотим, потому что сетевой запрос блокирует основную функцию, а это означает, что все последующие коды не могут продолжать нормально выполняться.
2.1.2 Асинхронные сетевые запросы
Давайте улучшим наш код выше, код выглядит следующим образом:
- Единственное отличие от только что приведенного кода состоит в том, что я использую объект Future, чтобы поместить трудоемкую операцию в переданную функцию;
- Позже мы объясним некоторые из его конкретных API, и мы временно знаем, что я создал экземпляр Future;
import "dart:io";
main(List<String> args) {
print("main function start");
print(getNetworkData());
print("main function end");
}
Future<String> getNetworkData() {
return Future<String>(() {
sleep(Duration(seconds: 3));
return "network data";
});
}
Посмотрим на результаты выполнения кода:
- 1. На этот раз код выполняется последовательно без каких-либо блокировок;
- 2. В отличие от предыдущего результата прямой печати, на этот раз мы печатаем экземпляр Future;
- Вывод: мы изолировали трудоемкую операцию, которая больше не повлияет на выполнение нашего основного потока.
- Вопрос: Как мы можем получить конечный результат?
main function start
Instance of 'Future<String>'
main function end
Получите результат, полученный Будущим
С Future, как получить результат запроса: через обратный вызов .then:
main(List<String> args) {
print("main function start");
// 使用变量接收getNetworkData返回的future
var future = getNetworkData();
// 当future实例有返回结果时,会自动回调then中传入的函数
// 该函数会被放入到事件循环中,被执行
future.then((value) {
print(value);
});
print(future);
print("main function end");
}
Результат выполнения приведенного выше кода:
main function start
Instance of 'Future<String>'
main function end
// 3s后执行下面的代码
network data
Во время выполнения произошло исключение
Если во время вызывающего процесса возникает исключение, и результат не может быть получен, как получить информацию об исключении?
import "dart:io";
main(List<String> args) {
print("main function start");
var future = getNetworkData();
future.then((value) {
print(value);
}).catchError((error) { // 捕获出现异常时的情况
print(error);
});
print(future);
print("main function end");
}
Future<String> getNetworkData() {
return Future<String>(() {
sleep(Duration(seconds: 3));
// 不再返回结果,而是出现异常
// return "network data";
throw Exception("网络请求出现错误");
});
}
Результат выполнения приведенного выше кода:
main function start
Instance of 'Future<String>'
main function end
// 3s后没有拿到结果,但是我们捕获到了异常
Exception: 网络请求出现错误
2.1.3 Дополнение к будущему использованию
Приложение 1: Резюме вышеупомянутого дела
Мы использовали прецедент, чтобы изучить некоторые аспекты использования Future:
- 1. Создать Future (он может быть создан нами, а может быть Future, полученный путем вызова внутреннего API или стороннего API. Короче говоря, вам нужно получить экземпляр Future, а Future обычно инкапсулирует некоторые асинхронные операции );
- 2. Следить за результатом, полученным при завершении внутреннего выполнения Future с помощью .then (успешная функция обратного вызова);
- 3. Используйте .catchError (функция обратного вызова сбоя или исключения) для отслеживания информации об ошибке внутреннего сбоя или исключения выполнения Future;
Дополнение 2: Два состояния Будущего
На самом деле, в течение всего процесса выполнения Future мы обычно делим его на два состояния:
Укажите один:未完成状态(uncompleted)
- При выполнении операции внутри Future (в приведенном выше случае это конкретный процесс сетевого запроса, мы используем задержку для имитации), мы называем этот процесс незавершенным состоянием.
Укажите два:Завершенный статус (завершено)
- Когда операция внутри Future завершается, она обычно возвращает значение или выдает исключение.
- В обоих случаях мы называем Будущее завершенным состоянием.
На официальном сайте Dart есть анализ этих двух состояний, причина, по которой он опубликован, заключается в том, что он отличается от трех состояний Promise.
Дополнение 3: Цепной вызов будущего
Приведенный выше код можно улучшить следующим образом:
- Затем мы можем продолжить возвращать значение, и мы получим возвращенный результат в следующей цепочке, а затем вызовем функцию обратного вызова.
import "dart:io";
main(List<String> args) {
print("main function start");
getNetworkData().then((value1) {
print(value1);
return "content data2";
}).then((value2) {
print(value2);
return "message data3";
}).then((value3) {
print(value3);
});
print("main function end");
}
Future<String> getNetworkData() {
return Future<String>(() {
sleep(Duration(seconds: 3));
// 不再返回结果,而是出现异常
return "network data1";
});
}
Результат печати следующий:
main function start
main function end
// 3s后拿到结果
network data1
content data2
message data3
Дополнение 4: Другие API будущего
Future.value(value)
- Получить завершенное Future напрямую, которое будет напрямую вызывать функцию обратного вызова then
main(List<String> args) {
print("main function start");
Future.value("哈哈哈").then((value) {
print(value);
});
print("main function end");
}
Результат печати следующий:
main function start
main function end
哈哈哈
Сомнение: зачем делать это сразу, а哈哈哈
Это напечатано в конце?
- Это связано с тем, что «Тогда в Будущем» будет добавлено в Очередь событий как новая задача, и после присоединения вам обязательно нужно будет встать в очередь на выполнение.
Future.error(object)
- Получите завершенное Future напрямую, но это Future с исключением, и Future будет напрямую вызывать функцию обратного вызова catchError
main(List<String> args) {
print("main function start");
Future.error(Exception("错误信息")).catchError((error) {
print(error);
});
print("main function end");
}
Результат печати следующий:
main function start
main function end
Exception: 错误信息
Future.delayed(时间, 回调函数)
- Функция обратного вызова выполняется после определенной задержки, а обратный вызов then выполняется после выполнения функции обратного вызова;
- В предыдущем случае мы также можем использовать его для симуляции, но непосредственное изучение этого API еще больше запутает всех;
main(List<String> args) {
print("main function start");
Future.delayed(Duration(seconds: 3), () {
return "3秒后的信息";
}).then((value) {
print(value);
});
print("main function end");
}
2.2 ждать, асинхронно
2.2.1 Понимание теоретических концепций
Если вы полностью поняли Future, вам не составит труда изучить await и async.
Что такое ожидание и асинхронность?
- Они являются ключевыми словами в Dart (Разве ты не ерунда? На ерунду все же следует обратить внимание, если ты используешь ее как имя переменной, невинное лицо.)
- Они позволяют нам использовать
同步的代码格式
, реализовать异步的调用过程
. - Кроме того, обычно асинхронная функция возвращает Future (не волнуйтесь, вы скоро увидите код).
Мы уже знаем, что Future не может блокировать наш поток, позволяя потоку продолжать выполняться и изменять свое состояние, когда он завершает операцию, а затем выполнять обратный вызов или обратный вызов errorCatch.
Как создать Будущее?
- 1. Вы можете использовать конструктор Future, который мы изучили ранее, или API Future, о которых мы узнали позже.
- 2. Так же есть функция через асинхронность.
2.2.2 Пример пошагового руководства по коду
Talk is cheap. Show me the code.
Преобразуем предыдущий код асинхронной обработки Future в форму await и async.
Мы знаем, что если вы напишете код прямо так, код не будет выполняться нормально:
- Поскольку Future.delayed возвращает объект Future, мы не можем думать о нем как о синхронных возвращаемых данных:
"network data"
использовать - То есть мы не можем использовать этот асинхронный код, как если бы он был синхронным!
import "dart:io";
main(List<String> args) {
print("main function start");
print(getNetworkData());
print("main function end");
}
String getNetworkData() {
var result = Future.delayed(Duration(seconds: 3), () {
return "network data";
});
return "请求到的数据:" + result;
}
Теперь я использую await для изменения следующего кода:
- вы обнаружите, что я
Future.delayed
Перед функцией добавляется ожидание. - Как только это ключевое слово доступно, операция ожидает
Future.delayed
завершает свое выполнение и ожидает его результата.
String getNetworkData() {
var result = await Future.delayed(Duration(seconds: 3), () {
return "network data";
});
return "请求到的数据:" + result;
}
После изменения и выполнения кода вы увидите следующую ошибку:
- Ошибка довольно очевидна: ключевое слово await должно присутствовать в асинхронной функции.
- Поэтому нам нужно
getNetworkData
Функции определяются как асинхронные функции.
Продолжайте изменять код следующим образом:
- Это также очень просто, просто добавьте ключевое слово async после функции ()
String getNetworkData() async {
var result = await Future.delayed(Duration(seconds: 3), () {
return "network data";
});
return "请求到的数据:" + result;
}
Запустив код, он все еще сообщает об ошибке (думая: ваша сестра):
- Ошибка очевидна: функция, помеченная как async, должна возвращать объект Future.
- Поэтому нам нужно продолжать изменять код и записывать возвращаемое значение как Future.
Продолжайте изменять код следующим образом:
Future<String> getNetworkData() async {
var result = await Future.delayed(Duration(seconds: 3), () {
return "network data";
});
return "请求到的数据:" + result;
}
Этот код должен быть кодом, который мы идеально выполняем
- Теперь мы можем использовать результаты, возвращаемые Future, асинхронно, как синхронный код;
- Подождите, пока результат будет склеен с другими данными, а затем вернитесь вместе;
- При возврате вам не нужно оборачивать Future, просто верните его напрямую, но возвращаемое значение по умолчанию будет заключено в Future;
2.3. Чтение json-кейса
Здесь я привожу случай чтения локального json-файла в проекте Flutter, преобразования его в объект модели и его возврата;
Этот кейс используется в качестве ссылки для всех, чтобы изучить предыдущий Future, await и async, Я не собираюсь его расширять, потому что необходимо использовать соответствующие знания Flutter;
Позже я объясню это снова в следующих случаях в процессе моего использования во Flutter;
Прочитайте код случая json (просто поймите его)
import 'package:flutter/services.dart' show rootBundle;
import 'dart:convert';
import 'dart:async';
main(List<String> args) {
getAnchors().then((anchors) {
print(anchors);
});
}
class Anchor {
String nickname;
String roomName;
String imageUrl;
Anchor({
this.nickname,
this.roomName,
this.imageUrl
});
Anchor.withMap(Map<String, dynamic> parsedMap) {
this.nickname = parsedMap["nickname"];
this.roomName = parsedMap["roomName"];
this.imageUrl = parsedMap["roomSrc"];
}
}
Future<List<Anchor>> getAnchors() async {
// 1.读取json文件
String jsonString = await rootBundle.loadString("assets/yz.json");
// 2.转成List或Map类型
final jsonResult = json.decode(jsonString);
// 3.遍历List,并且转成Anchor对象放到另一个List中
List<Anchor> anchors = new List();
for (Map<String, dynamic> map in jsonResult) {
anchors.add(Anchor.withMap(map));
}
return anchors;
}
3. Асинхронное дополнение Dart
3.1. Порядок выполнения задания
3.1.1 Понимание очередей микрозадач
В предыдущем исследовании мы знали, что в Dart есть цикл событий (Event Loop) для выполнения нашего кода, а в нем есть очередь событий (Event Queue), и цикл событий непрерывно берет события из очереди событий на выполнение .
Но если строго разделить, то в Dart есть еще одна очередь: Microtask Queue.
- приоритет очереди микрозадач выше, чем у очереди событий;
- то есть
事件循环
имеют приоритет微任务队列
задача в , а затем выполнить事件队列
задачи в;
Итак, при разработке Flutter, какие из них помещаются в очередь событий, а какие — в очередь микрозадач?
- Все задачи внешних событий находятся в очереди событий, такие как события ввода-вывода, таймера, кликов и рисунков;
- Микрозадачи обычно создаются внутри Dart, а микрозадач очень мало. Это связано с тем, что если микрозадач слишком много, очередь событий не будет поставлена в очередь, а выполнение очереди задач будет заблокировано (например, когда пользователь нажимает и не отвечает);
Говоря об этом, вы, возможно, немного запутались.Как код выполняется в одном потоке Dart?
- 1. Ввод Dart является основной функцией, поэтому
main函数中的代码
будет иметь приоритет; - 2. После выполнения основной функции будет запущен цикл событий (Event Loop), а задачи в очереди будут выполняться после запуска;
- 3. Во-первых, он будет выполняться в порядке «первым поступил — первым вышел».
微任务队列(Microtask Queue)
все задачи в; - 4. Во-вторых, он будет выполняться в порядке «первым пришел — первым вышел».
事件队列(Event Queue)
все задачи в;
3.1.2 Как создавать микрозадачи
В разработке мы можем создать микрозадачу через scheduleMicrotask под асинхронностью в dart:
import "dart:async";
main(List<String> args) {
scheduleMicrotask(() {
print("Hello Microtask");
});
}
В процессе разработки, если у нас есть задача, которую мы не хотим ставить в очередь в очереди событий, мы можем создать микрозадачу.
Добавляется ли код Future в очередь событий или очередь микрозадач?
Обычно в Future есть два тела выполнения функции:
- Тело функции, переданной конструктором Future.
- Тело функции then (то же самое, что и catchError)
Так в какую очередь они добавлены?
- Тело функции, переданное конструктором Future, помещается в очередь событий.
- Тело функции then разделено на три случая:
- Случай 1: Future не выполняется (есть задачи, которые нужно выполнить), а затем будет напрямую добавлено в тело выполнения функции Future;
- Случай 2: если Future выполняется, а затем выполняется, тело функции then помещается в очередь микрозадач, а очередь микрозадач выполняется после выполнения текущего Future;
- Случай 3: Если Future вызывается в цепочке, это означает, что then не выполнено, и следующее then не будет выполнено;
// future_1加入到eventqueue中,紧随其后then_1被加入到eventqueue中
Future(() => print("future_1")).then((_) => print("then_1"));
// Future没有函数执行体,then_2被加入到microtaskqueue中
Future(() => null).then((_) => print("then_2"));
// future_3、then_3_a、then_3_b依次加入到eventqueue中
Future(() => print("future_3")).then((_) => print("then_3_a")).then((_) => print("then_3_b"));
3.1.3 Порядок выполнения кода
Мы изучаем极的代码执行顺序
Случай:
import "dart:async";
main(List<String> args) {
print("main start");
Future(() => print("task1"));
final future = Future(() => null);
Future(() => print("task2")).then((_) {
print("task3");
scheduleMicrotask(() => print('task4'));
}).then((_) => print("task5"));
future.then((_) => print("task6"));
scheduleMicrotask(() => print('task7'));
Future(() => print('task8'))
.then((_) => Future(() => print('task9')))
.then((_) => print('task10'));
print("main end");
}
Результат выполнения кода:
main start
main end
task7
task1
task6
task2
task3
task5
task4
task8
task9
task10
Анализ кода:
- 1. Основная функция выполняется первой, поэтому
main start
иmain end
Выполните сначала, нет проблем; - 2. Выполнение основной функции
过程中
, некоторые задачи будут добавлены вEventQueue
иMicrotaskQueue
середина; - 3. Задание7 пройдено
scheduleMicrotask
вызов функции, поэтому он добавляется к самому раннемуMicrotaskQueue
, будет выполнен первым; - 4. Затем начните выполнение
EventQueue
, задача1 добавлена вEventQueue
быть казненным; - 5. Пройти
final future = Future(() => null);
Затем к микрозадаче добавляется созданное будущее, и микрозадача выполняется непосредственно первой, поэтому будет выполняться задача 6; - 6. Однажды в
EventQueue
Добавить задачу2, задачу3, задачу5 для выполнения; - 7. После выполнения печати задачи3 вызовите
scheduleMicrotask
, то после выполнения этогоEventQueue
будет выполнен позже, поэтому задача4 будет выполнена после задачи5 (примечание:scheduleMicrotask
Вызов является частью кода задачи3, поэтому задача4 должна выполняться после задачи5) - 8. Task8, task9, task10 добавляются в
EventQueue
быть казненным;
На самом деле в интервью может появиться указанный выше порядок выполнения кода, у нас обычно нет такой сложной вложенности в нашей разработке, и нам нужно полностью понимать порядок его выполнения;
Однако знание порядка выполнения приведенного выше кода даст вамEventQueue
иmicrotaskQueue
иметь более глубокое понимание.
3.2 Использование многоядерного процессора
3.2.1. Понимание изоляции
В Dart есть понятие Isolate, что это?
- Мы уже знаем, что Dart является однопоточным, и у этого потока есть собственное пространство памяти, к которому он может получить доступ, и цикл обработки событий, который необходимо запустить;
- Мы можем назвать эту космическую систему Изолятом;
- Например, во Flutter есть Root Isolate, который отвечает за выполнение кода Flutter, такого как рендеринг пользовательского интерфейса, взаимодействие с пользователем и т. д.;
В Isolate очень хорошо реализована изоляция ресурсов, у каждого Isolate есть собственный цикл событий и очередь,
- Изоляты не совместно используют какие-либо ресурсы и могут полагаться только на механизм сообщений для связи, поэтому проблема вытеснения ресурсов отсутствует.
Однако, если есть только один Isolate, это означает, что мы можем использовать только один поток навсегда, что является пустой тратой ресурсов для многоядерных процессоров.
Если у нас много трудоемких вычислений во время разработки, мы можем сами создать Isolate и выполнять нужные расчетные операции в отдельном Isolate.
Как создать изолят?
Создать Isolate относительно просто, мы передаемIsolate.spawn
создавать:
import "dart:isolate";
main(List<String> args) {
Isolate.spawn(foo, "Hello Isolate");
}
void foo(info) {
print("新的isolate:$info");
}
3.2.2 Изолировать механизм связи
Но в реальной разработке мы не будем просто запускать новый Isolate, не заботясь о его рабочем результате:
- Нам нужен новый Изолят для расчета, и сообщить Главному Изоляту результат расчета (то есть Изолят включен по умолчанию);
- Isolate реализует механизм передачи сообщений через SendPort;
- Мы можем передать конвейер отправки Main Isolate в качестве параметра при запуске параллельного Isolate;
- Когда параллелизм завершен, вы можете использовать этот конвейер для отправки сообщения в основной изолятор;
import "dart:isolate";
main(List<String> args) async {
// 1.创建管道
ReceivePort receivePort= ReceivePort();
// 2.创建新的Isolate
Isolate isolate = await Isolate.spawn<SendPort>(foo, receivePort.sendPort);
// 3.监听管道消息
receivePort.listen((data) {
print('Data:$data');
// 不再使用时,我们会关闭管道
receivePort.close();
// 需要将isolate杀死
isolate?.kill(priority: Isolate.immediate);
});
}
void foo(SendPort sendPort) {
sendPort.send("Hello World");
}
Но наше общение, описанное выше, стало односторонним. Что делать, если требуется двустороннее общение?
- На самом деле код для двусторонней связи будет более проблематичным;
- Flutter обеспечивает поддержку параллельных вычислений
compute
функция, которая внутренне инкапсулирует создание и двустороннюю коммуникацию Isolate; - Используя его, мы можем в полной мере использовать многоядерный процессор, и он очень прост в использовании;
Примечание. Следующий код — это не API dart, а API Flutter, поэтому его можно запустить только в проекте Flutter.
main(List<String> args) async {
int result = await compute(powerNum, 5);
print(result);
}
int powerNum(int num) {
return num * num;
}
Примечания: Весь контент сначала публикуется на официальном аккаунте, кроме Flutter позже будут обновляться и другие технические статьи, такие как TypeScript, React, Node, uniapp, mpvue, структуры данных и алгоритмы и т.д., а также некоторые собственные опыт обучения также будет обновлен.Приглашаем всех обратить внимание