> For the complete documentation index, see [llms.txt](https://ryukiedev.gitbook.io/wiki/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ryukiedev.gitbook.io/wiki/flutter/20.dart-yi-bu-bian-cheng-isolates-he-shi-jian-xun-huan.md).

# 20.Dart异步编程:Isolates和事件循环

### 前言

Dart 是单线程语言，它为 Futures Steams 后台作业，以及编写现代化、异步、响应式的 Flutter 应用程序所需的其他内容提供支持。

![1](/files/Q35QIV14iP9X8bGQaenY)

### Isolate

Isolate 是所有 Dart 代码运行的地方。它就像机器中的一个小空间，它拥有自己的`内存块`和运行`事件循环`的`单一线程`。

在其他语言中，比如 C++ 你可以让多个线程共享相同的内存。并运行他们想要的代码。但是在 Dart 中，每个线程都有自己的 Isolate 和它自己的内存，它只处理事件。

许多 Dart 应用程序，在单一 Isolate 中运行所有代码。但是如果需要，你可以使用多个 Isolate。

![2](/files/VQBNbhpMSw6gMbon81N2)

如果你要运行的计算量太过庞大，如果它在主要的 Isolate 中运行的话，会导致应用程序扔掉当前的栈帧（Drop Frame）

#### 子Isolate

你可以使用 Isolate.spawn 或者 Flutter 的计算函数，两者都会创建一个单独的 Isolate 来进行运算。让你主要的 Isolate 可以不受影响。

![3](/files/uIvww1zWOdbWWtNoOvjp)

新的 Isolate 将会获得独立的事件循环和自己的内存，即使原本的 Isolate 是这个新 Isolate 的母体，原本的 Isolate 也不可以访问它。

![4](/files/4KLZ950iPTcNpLx8PZM3)

> 这就是 Isolate 名称的来源。这些小空间之间彼此隔离。

#### Isolate 间通讯

Isolate 间合作的唯一方式，是来回传递消息。

一个 Isolate 会给另一个 Isolate 发送消息，接收消息的 Isolate 会使用他的事件循环处理消息。

![5](/files/aJuPEC11aSmWE1sqBLwO)

#### Isolate 为什么这样设计

缺乏共享内存，可能听起来有些严格。 特别如果你是使用 Java C++ 这样的语言的话。但它对 Dart 编译器有一些关键性的好处。

比如:

* Isolate 的内存分配和垃圾回收不需要`锁`
* 因为他只有单一线程，因此如果它不忙，你就知道内存没有发生变化。这对 Flutter 应用程序非常有用。它有时需要快速构建和拆解很多 Widget。

### 事件循环

![6](/files/W3w5HtUBYGh6N5pujrRm)

想象一下，应用程序的生命，在时间轴中延伸。期间会有很多外部信息的输入，而我们并不能预知这些信息什么时候会输入。这时我们就需要一个永不阻塞的线程来处理所有事件，它运行一个事件循环。

![7](/files/FacHGaV9rnP5DsaqHwyJ)

他从事件队列中，取出最旧的事件，处理它，返回，再取出下一个事件处理，依次循环，直到队列清空。

当事件清空，事件循环就会进入等待，直到有新的事件加入队列。

所有高级API都可用于异步编程。他们全部建立在这个简单的循环上。

### Isolate 的使用

#### 真多线程

```
void main() {
  print('${DateTime.now()}: 1');

  Isolate.spawn(isolateFunc, 15);

  sleep(Duration(seconds: 3));
  print('${DateTime.now()}: Sleep end');
}

void isolateFunc(int value) {
  sleep(Duration(seconds: 1));
  print('${DateTime.now()}: isolateFunc end $value');
}
```

通过日志我们发现：

```
flutter: 2021-11-28 10:12:09.313570: 1
flutter: 2021-11-28 10:12:10.422793: isolateFunc end 15
flutter: 2021-11-28 10:12:12.349747: Sleep end
```

sleep 的3秒并没有影响到 Isolate 调用，这里真正实现了多线程。

#### 资源竞争

在进行 iOS 多线程开发的时候，我们会遇到资源竞争的问题。我们通常通过加锁来保证逻辑的正常。那么在使用 Isolate 的时候会不会也遇到这样的情况呢？

```
int number = 1;

void main() {
  print('${DateTime.now()}: 1');

  Isolate.spawn(isolateFunc, 1);
  Isolate.spawn(isolateFunc, 2);
  Isolate.spawn(isolateFunc, 3);
  Isolate.spawn(isolateFunc, 4);
  Isolate.spawn(isolateFunc, 5);
  Isolate.spawn(isolateFunc, 6);
  Isolate.spawn(isolateFunc, 7);
  Isolate.spawn(isolateFunc, 8);
  Isolate.spawn(isolateFunc, 9);
  Isolate.spawn(isolateFunc, 10);

  sleep(Duration(seconds: 5));
  print('${DateTime.now()}: Sleep end，number = $number');
}

void isolateFunc(int value) {
  number += 1;
  print('${DateTime.now()}: 第$value个调用， number = $number');
}
```

按照 iOS 开发经验理解，上面的输出应该是乱序的，而且 number 的读写也是会比较乱的。且 sleep 5秒后输出的 number 值会 +10。

我们看下输出：

```
flutter: 2021-11-28 10:24:18.275046: 1
flutter: 2021-11-28 10:24:18.506326: 第1个调用， number = 2
flutter: 2021-11-28 10:24:18.543709: 第3个调用， number = 2
flutter: 2021-11-28 10:24:18.572635: 第4个调用， number = 2
flutter: 2021-11-28 10:24:18.589551: 第5个调用， number = 2
flutter: 2021-11-28 10:24:18.613625: 第7个调用， number = 2
flutter: 2021-11-28 10:24:18.613557: 第8个调用， number = 2
flutter: 2021-11-28 10:24:18.618459: 第2个调用， number = 2
flutter: 2021-11-28 10:24:18.645893: 第6个调用， number = 2
flutter: 2021-11-28 10:24:18.669942: 第10个调用， number = 2
flutter: 2021-11-28 10:24:18.674398: 第9个调用， number = 2
flutter: 2021-11-28 10:24:23.296321: Sleep end，number = 1
```

* 确实是乱序执行的，符合多线程的特征
* number 值全部都只 +1 了，都是2
  * 这里就关系到前文有提到的 Isolate 的特性之一：
    * 含有独立的内存空间
    * 这里每个 Isolate 内的 number 都是独立的

#### 线程间通讯

这里的通讯需要通过端口 Port 来进行，并且完成后需要手动释放。

```
int number = 1;

void main() async {
  print('${DateTime.now()}: 1');

  // 创建端口
  ReceivePort port = ReceivePort();
  // Isolate
  Isolate iso = await Isolate.spawn(isolateFunc, port.sendPort);
  // 监听数据变化
  port.listen((message) {
    number = message;
    print('${DateTime.now()}: 监听到 number = $number');
    // 关闭端口
    port.close();
    // 销毁 Isolate
    iso.kill();
  });
}

void isolateFunc(SendPort send) {
  sleep(Duration(seconds: 3));
  send.send(15);
}
```

输出：

```
flutter: 2021-11-28 10:38:59.982321: 1
flutter: 2021-11-28 10:39:03.114209: 监听到 number = 15
```

### compute

compute 是对 Isolate 的封装可以达到一样的效果：

```
int number = 0;

void main() async {
  print('${DateTime.now()}: 1');

  compute(comFunc, 1);
  compute(comFunc, 2);
  compute(comFunc, 3);
  compute(comFunc, 4);
  compute(comFunc, 5);
  compute(comFunc, 6);
  compute(comFunc, 7);
  compute(comFunc, 8);
  compute(comFunc, 9);
  compute(comFunc, 10);
}

void comFunc(int value) {
  number = value;
  print('${DateTime.now()}: value = $value number = $number');
}
```

输出：

```
flutter: 2021-11-28 10:54:03.331916: value = 6 number = 6
flutter: 2021-11-28 10:54:03.337162: value = 5 number = 5
flutter: 2021-11-28 10:54:03.382666: value = 4 number = 4
flutter: 2021-11-28 10:54:03.404757: value = 1 number = 1
flutter: 2021-11-28 10:54:03.406688: value = 7 number = 7
flutter: 2021-11-28 10:54:03.424210: value = 3 number = 3
flutter: 2021-11-28 10:54:03.433246: value = 8 number = 8
flutter: 2021-11-28 10:54:03.447492: value = 2 number = 2
flutter: 2021-11-28 10:54:03.494157: value = 9 number = 9
flutter: 2021-11-28 10:54:03.495839: value = 10 number = 10
```

#### 进程间通讯

可以通过返回值的形式进行：

```
int number = 0;

void main() async {
  number = await compute(comFunc, 1);

  print(number);
}

int comFunc(int value) {
  sleep(Duration(seconds: 2));
  return number + value;
}
```

输出：

```
flutter: 1
```

### 参考

<https://medium.com/dartlang/dart-asynchronous-programming-isolates-and-event-loops-bffc3e296a6a>

<https://www.youtube.com/watch?v=vl\\_AaCgudcY\\&t=53s>
