写点什么

dart 系列之:dart 优秀的秘诀 - 隔离机制

作者:程序那些事
  • 2021 年 12 月 16 日
  • 本文字数:2577 字

    阅读完需:约 8 分钟

dart系列之:dart优秀的秘诀-隔离机制

简介

之前介绍了很多 dart 中的异步编程技巧,不知道大家有没有发现一个问题,如果是在 java 的异步编程中,肯定会提到锁和并发机制,但是对于 dart 来说,好像从来没有听到多线程和并发的问题,这是为什么呢?


今天,给大家讲解一下 dart 中的隔离机制,大家就明白了。

dart 中的隔离机制

dart 是一个单线程的语言,但是作为一个单线程的语言,dart 却支持 Future,Stream 等异步特性。这一切都是隔离机制和事件循环带来的结果。


首先看一下 dart 中的隔离机制。


所谓隔离指的是 dart 运行的一个特定的空间,这个空间拥有单独的内存和单线程的事件循环。


如下图所示:


在 java 或者 c++等其他语言中,多个线程是共享内存空间的,虽然带来了并发和数据沟通的方便途径,但是同时也造成了并发编程的困难。


因为我们需要考虑多线程之间数据的同步,于是额外多出了很多锁的机制,详细了解或者用过的人应该都会很烦恼。


多线程最大的缺陷就是要求程序员的罗辑思维和编程技巧足够优秀,这样才能够设计出完美运行的多线程程序。


但是在 dart 中,这些都不是什么问题。dart 中所有的线程都拥有自己的运行空间,这个线程的工作就是运行事件循环。


那么问题来了,主线程在处理事件循环,但是如果遇到了一个非常耗时的操作,该怎么办呢? 如果直接在主线程中运行,则可能会导致主线程的阻塞。


dart 也充分考虑到了这个问题,所以 dart 提供了一个 Isolate 的类来对隔离进行管理。


因为 dart 程序本身就在一个 Isolate 中运行,所以如果在 dart 中定义一个 Isolate,那么这个 Isolate 通常表示的是另外一个,需要和当前 Isolate 进行通信的 Isolate。

生成一个 Isolate

那么如何在当前的 dart 程序中生成一个 Isolate 呢?


Isolate 提供了三种生成方法。


一个非常常用的是 Isolate 的工厂方法 spawn:


  external static Future<Isolate> spawn<T>(      void entryPoint(T message), T message,      {bool paused = false,      bool errorsAreFatal = true,      SendPort? onExit,      SendPort? onError,      @Since("2.3") String? debugName});
复制代码


spawn 会创建一个新的 Isolate,调用它需要传入几个参数:


entryPoint 表示的是生成新 Isolate 的时候需要调用的函数。entryPoint 接受一个 message 参数。通常来说 message 是一个 SendPort 对象,用于两个 Isolate 之间的沟通。


paused 表示新生成的 Isolate 是否处于暂停状态,他相当于:


isolate.pause(isolate.pauseCapability)
复制代码


如果后续需要取消暂停状态,则可以调用:


isolate.resume(isolate.pauseCapability)
复制代码


errorsAreFatal 对应的是 setErrorsFatal 方法。


onExit 对应的是 addOnExitListener, onError 对应的是 addErrorListener。


debugName 表示的是 Isolate 在调试的时候展示的名字。


如果 spawn 出错,则会抛出 IsolateSpawnException 异常:


class IsolateSpawnException implements Exception {  /// Error message reported by the spawn operation.  final String message;  @pragma("vm:entry-point")  IsolateSpawnException(this.message);  String toString() => "IsolateSpawnException: $message";}
复制代码


spawn 方法生成的是和当前代码一样的 Isolate。如果想要使用不同的代码来生成,则可以使用 spawnUri,通过传入对应的 Uri 地址,从而生成不一样的 code。


external static Future<Isolate> spawnUri(      Uri uri,      List<String> args,      var message,      {bool paused = false,      SendPort? onExit,      SendPort? onError,      bool errorsAreFatal = true,      bool? checked,      Map<String, String>? environment,      @Deprecated('The packages/ dir is not supported in Dart 2')          Uri? packageRoot,      Uri? packageConfig,      bool automaticPackageResolution = false,      @Since("2.3")          String? debugName});
复制代码


还有一种方式,就是使用 Isolate 的构造函数:


Isolate(this.controlPort, {this.pauseCapability, this.terminateCapability});
复制代码


它有三个参数,第一个参数是 controlPort,代表另外一个 Isolate 的控制权,后面两个 capabilities 是原 isolate 的子集,表示是否有 pause 或者 terminate 的权限。


一般用法如下:


Isolate isolate = findSomeIsolate();Isolate restrictedIsolate = Isolate(isolate.controlPort);untrustedCode(restrictedIsolate);
复制代码

Isolate 之间的交互

所有的 dart 代码都是运行在 Isolate 中的,然后代码只能够访问同一个 isolate 内的 class 和 value。那么多个 isolate 之间通信,可以 ReceivePort 和 SendPort 来实现。


先看下 SendPort,SendPort 是 Capability 的一种:


abstract class SendPort implements Capability 
复制代码


SendPort 用于向 ReceivePort 发送 message, message 可以有很多类型,包括:


Null,bool,int,double,String,List,Map,TransferableTypedData,SendPort 和 Capability。


注意,send 动作是立马完成的。


事实上,SendPort 是由 ReceivePort 来创建的。一个 ReceivePort 可以接收多个 SendPort。


ReceivePort 是 Stream 的一种:


abstract class ReceivePort implements Stream<dynamic>
复制代码


作为 Stream,它提供了一个 listen 用来处理接收到的消息:


  StreamSubscription<dynamic> listen(void onData(var message)?,      {Function? onError, void onDone()?, bool? cancelOnError});
复制代码

一个例子

讲了那么多原理,有的同学可能会问了,那么到底怎么用呢?


例子来了:


import 'dart:isolate';
var isolate;
void entryPoint(SendPort sendPort) { int counter = 0; sendPort.send("counter:$counter");}void main() async{ final receiver = ReceivePort(); receiver.listen((message) { print( "接收到消息 $message"); }); isolate = await Isolate.spawn(entryPoint, receiver.sendPort);}
复制代码


在主线程中,我们创建了一个 ReceivePort,然后调用了它的 listen 方法来监听 sendPort 发过来的消息。


然后 spawn 出一个新的 Isolate,这个 Isolate 会在初始化之后,调用 entryPoint 方法。


在这个 entryPoint 方法中又使用 sendPort 向 ReceivePort 发送消息。


最终运行,打印:


接收到消息 counter:0
复制代码

总结

以上就是 dart 中的隔离机制和 Isolate 的使用。


本文已收录于 http://www.flydean.com/25-dart-isolates/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

发布于: 19 小时前阅读数: 16
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
dart系列之:dart优秀的秘诀-隔离机制