为什么 Dart 是单线程的语言却能支持异步?

2,740 阅读3分钟

在开始弄懂问题之前我们先来了解下两个非常重要的东西:

isolateevent loop

Isolate

如果你已经看过官方文档,相信已经对 isolate 有了一定的了解,这里,我想再重新讲一下。

ioslate 被翻译成中文是隔离器的意思,下文会统称为隔离器

隔离器我们从字面上可以隐约感知到,它是一个单独的被隔离的器皿。是的,在Dart中,它就是这么一个被隔离的东西。

那么隔离的东西里面有什么呢?

它有一块自己的内存(Memory)以及一个单独的线程(Thread)。

那这个线程是干嘛的呢?

答案: 它一直在运行事件循环(Event Loop)

如下图所示:

隔离器是不是唯一的?

在 Dart app 中隔离器不是唯一的,可以手动创建多个隔离器。

利用 Isolate.spawn() 或者 Flutter 中的 compute() 来创建多个隔离器。

为什么要创建多个隔离器?

试想一下,如果只有一个隔离器,那么做庞大计算时是不是很容易造成掉帧(计算密集型操作会让 App 卡顿)

当然也可以从上面两个创建隔离器的函数名中看出端倪。

创建出的隔离器与原先的隔离器相互之间不能访问各自的内存及线程,它们只有一种交互模式

通过互相传递消息,A 隔离器发送一条消息给 B 隔离器,B 隔离器接收到消息后利用自己的事件循环来处理消息。

这样的好处是,能够不用锁定隔离器中的内存分配及垃圾回收,因为只有一个线程,所以很容易就能知道内存有没有被修改(因为没有别的线程能访问这块内存空间),在 Flutter 中,对快速构建或拆除一个界面中的部分组件特别有效。

Event loop

上文中已经有提到事件循环,相信你已经对它有点印象了,它会负责处理收到的消息事件,当然它也是让 Dart 能够真正拥有异步能力的核心。

1. 事件循环是干嘛用的?

在一个 app 中,启动,退出,用户会点击,滑动,程序访问硬盘等等,这一系列的操作我们统称为事件,因为你不知道事件何时会发送过来,所以得有一个永不停歇不能阻塞的循环来等待处理事件。

2. 事件循环的运行机制

事件被被扔到一个事件队列中,而事件循环会从事件队列中取出最旧的事件处理,再返回下一个事件处理,以此类推直到事件队列中再也没有事件了。

当处理事件操作中存在一个中断(break),线程会先挂出,等待下一个事件。

3. 到底怎么异步啊?

相信绝大多数人看到这里还是没有想通为什么,综上,我就来讲解一下。

首先一个App从启动到关闭,绝大部分的时间都在等待中。 例如:等待用户操作,网络请求等等。

那么这种等待是不是阻塞的呢?肯定不是咯,否则还怎么玩!

刚才说了事件循环是永不停歇的,A 事件处理完后悔处理 B 事件,B 事件处理完了要处理 C事件。

这里传递的事件都不能够是阻塞型的事件,否则会造成 ANR,耗时操作记得使用异步对象。

Dart 中提供了一异步对象例如:FutureStream他们都是非阻塞的。

这些异步对象都会告诉事件循环说:待会来执行代码,你先忙你的。

这样子就形成了一个异步的机制。

总结

本文浅出了 Dart 单线程语言中如何实现异步,如果你对异步还有相关疑问,欢迎关注我们的公众号:算法详解

定期更新 Dart 技术干货。