Dart并发基于isolate
,依赖于future
和stream
。
future是代表可能还没有发生的运算结果的对象。结果可能在未来的某个时间是可知的,因此被称为future。future在异步运算中是有用的。一旦做出执行异步运算的请求,就可以以同步的方式立即返回future,而实际运算可以稍后运行。future想请求异步操作的代码提供了一个句柄,即一个请求运算过程的引用。在请求的运算完成后,future可以用来访问结果。我们把这称为future已经完成或者已经解决。
如果future f1的运算返回另一个future f2呢?如果结果本身是future,则f1不会被完成。可能在onValue
被调用时,要被迫检查传入的参数是否是future,并通过另一个then()
来处理,等等,无限循环。在这种情况下,f1的完成值取决于f2的完成值。当f2的完成值不是future时,f1将以相同的值完成。因此说future是链式的,因为他们形成了一个依赖链。
future同样可以表示失败的运算,也定义了方法catchError
,并且接收一个闭包作为输入参数。如果future代表的运算抛出异常,则调用闭包,即onError。
Future(Function computation) {
Timer.run(() =>
try {
_completeWithValue(computation());
} catch (e) {
_completeWithError(e);
}
);
}
Dart应用程序由一个事件循环,只要存在可用的事件处理程序来处理正在进行的事件,它就会运行。事件处理程序针对各种事件:鼠标点击、键盘输入及其他。
可以定义给定时间段之后被触发的处理程序。一个重要的特殊情况是时间段为0.当时间段为0时,处理程序会抓住最早的机会无条件运行相应的任务可以使用Timer.run()
定义这样的处理程序。通过设置这样的处理程序而安排在将来执行的任务,会在事件循环的下一次迭代中被执行。通过构造函数Future.delayed()
可以安排任务在非零延迟之后执行。一旦任务开始,它将保持运行直到完成,任务从不被抢占。任务运行的时间段被称为一个回合。在一个回合中被安排的所有future,将被保证只在当前回合完成后才运行,除了microtask。
有一种机制可以确保future在本回合安排的代码之前执行。每个回合都有各自的microtask队列。在本回合结束时,程序控制被转交到主事件循环之前,microtask队列上的任务将被运行。可以使用Future.microtask()
来安排future在microtask队列上运行。要注意的是,限制microtask的个数和大小很重要。
stream是一个长度不确定的值列表。它可能是无限的,或者最终可能结束。可以订阅或监听stream,那意味着为stream注册一个或多个回调函数。当新数据追加到stream中时,这些函数将被调用。
将stream视为一个集合是很自然的。集合上的许多操作都适用于stream。
Dart支持actor风格的并发模型。运行中的Dart程序由一个或多个actor组成,它们被称为isolate。isolate是有着自己的内存和单线程控制的计算过程。术语isolate源于独立实体间的分离,因为isolate之间的内存在逻辑上是分离的。isolate中的代码是按顺序运行的,任何并发都是运行多个isolate的结果。因为Dart没有共享内存的并发,所以不需要锁而且没有发生竞争的可能性。由于isolate没有共享内存,所以它们之间可以通信的唯一方式是通过消息传递。Dart中的消息传递总是异步的。isolate没有阻塞接收机制,因此不会发生死锁。
一个isolate由多个port。Port是Dart isolate间通信的底层基础。Port有两种:send port和receive port。receive port是一个接收消息的stream;send port则允许将消息发送给isolate的receive port。send port可以被receive port生成,它将把所有消息发送给对应的receive port。
在isolate中启动另一个isolate被称为spawning。生成isolate时需要指定一个库,isolate会从该库的main()方法开始执行,这个库被称为isolate的根库。类Isolate提供了两种用于生成isolate的类方法:第1种是spawnUri()
,它基于给定库的URI来产生一个isolate;第2种是spawn
它根据当前isolate的根库产生一个isolate。
当一个isolate产生另一个isolate时,它有机会传递一些初始参数,这些关键参数是一种初始消息。对消息的定义可归纳为:消息可能是null、数字、布尔值、字符串、send port、消息列表或键值对都是消息的map。初始消息通常包括一个send port,新产生的isolate(spawnee)可以利用该send port将消息发送回生成它的isolate(spawner)。
函数可以使用asyn修饰符进行标记,标记后的函数是一个async函数。使用async函数可以在多个方面简化处理future和异步的任务。当调用async函数时,函数的代码不会立即执行。函数中的代码被安排在将来的某个时间执行。函数将一个在函数体执行成功或失败时被完成的future返回给调用者。函数自动生成该future,并立即返回给调用者。
await表达式允许我们像编写同步代码那样编写异步代码。执行await表达式允许我们在等待异步计算完成时暂停运行周围的函数。await只能在异步函数中使用。Dart编译器不会在其他地方接受等待。
当考虑异步函数的执行时,需要记住,它的主体已经被调度在下一个回合且独立于其调用者执行,所以当异步函数运行时,它的调用者早就返回了。特别是,当调试时,没有有意义的调用堆栈来引用,并且没有调用者返回。那么在异步函数中返回语句是什么意思?返回的是将来与async函数关联的一个值。像往常一样,没有嵌套表达式的返回被视为返回null的简写。同样,如果一个异步函数抛出一个它无法捕获的异常,那么函数将由抛出的对象完成。
异步Generator是Dart支持的另一种形式的异步函数。一个函数体标记有async *
修饰符的函数,将作为stream的生成函数。
给定一个stream,可以遍历其中的值。
await for (var i in naturals) { print('event loop $i'); }
每次将一个元素添加到naturals stream时,循环体都会运行。在每次迭代之后,包含该循环的函数将暂停执行,直到下一个元素可用或stream完成。
像await表达式,await-for循环只能出现在异步函数中。
Dart程序由一个或多个isolate组成,它们是actor模式的并发单元,每个都有自己的内存和单线程控制。isolate是Dart的并发和安全单元。isolate是非阻塞的,并通过异步消息传递进行通信。消息发送返回表示其预期结果的future,future支持回调,用于指定future完成时要采用的操作,无论是成功还是失败。stream为典型的Dart应用结构提供了高层抽象封装。async方法使得以同步方式编写异步程序称为可能。