Home dart 事件循环与并发
Post
Cancel

dart 事件循环与并发

Dart-Event Loop

Dart 程序中处处可见异步代码:库会返回 Future 对象,我们所写的代码也可以通过注册 handler 处理点击,文件 I/O ,timer 等事件。

通过本文了解 Dart 的事件循环架构,可以让你写出更好的异步代码,规避意外。本文你可以学到:处理 future 任务的参数,清楚任务执行的顺序。

阅读本文之前,须了解 Future and Error handling

[分支]: 如果有任务A和任务B需并行执行,但任务C需要A,B的结果才能执行,要怎么写?在Dart上似乎只能把A,B串行?因为没有线程,或者只能用更重的 isolate 来执行? Pool 代码验证:Pool 并不非用于并发。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
Function taskSimulators(String name, List<Duration>  timeToSleep ){
  if(timeToSleep == null){
    timeToSleep = <Duration>[
      Duration(milliseconds: Random().nextInt(100000)),
      Duration(milliseconds: Random().nextInt(100000)),
      Duration(milliseconds: Random().nextInt(100000))
    ];
  }
  return (){
    for(var d in timeToSleep){
      for(var j=0;j<999999;++j){
        insert(name,j);
      }
      Future.delayed(d);
      print("$name after delayted");
    }
  };
}

var pre = "AAAA";
insert(name,j){
  if(name.codeUnitAt(0) < pre.codeUnitAt(0)){
    print("concurrency !!!!! pre=$pre name=$name");
  }else if(name != pre){
    pre = name;
    print("old: $pre new: $name");
  }
}


main() async{
  final Pool pool = new Pool(2,timeout: Duration(seconds: 50));
  <String>["AAAA","BBBB","CCCC","DDDD","EEEE","FFFF","GGGG"].forEach(
      (name){
        var f = taskSimulators(name, null);
        pool.withResource(f);
        print("$name executing");
      }
  );
}

Worker 不支持2.0 TODO 学习使用 Isolate ,写一个并发库? (run code in parallel)

Dart 是单线程的,所有任务都会放进Event Loop中,当main函数执行完毕后,就开始依次执行Event Loop中的任务。Loop 中有两个队列:microtask queue / event queue 。Dart 会先执行 microtask 中的任务,然后才到 event 中的任务,即前者会有更高优先级。随时可插队。(TODO: 读一下 pool 的实现也许可以加深了解)。由于 dart 是单线程,所以 Future.delayed 的时间是不准的,可能会由于某个任务执行而延后很多(和js中settimeout一样)。

一些错误例子:

1
2
future.then(/* 为某变量赋值 */);
Timer.run((){ /* 使用上面的变量 */ });

以上代码的问题是,如果 future 内有 await 让出cpu,则timer 会先于then执行。正确的做法如下(第二个then 的 v 为第一个 then 返回的值)。

1
future.then((v){ /* 设置变量 */}).then((v){ /* 使用变量 */ });

如果使用变量的操作比较耗时,可考虑用 Future((){ /* 使用变量 */ } ); 包住。

Future(FutureOr computation()) 的实现为:在 Timer.run 中执行 computation 。

使用 Future 可以让 event loop 有机会执行其他任务。

是否可理解 best practice 为:不要在future 的 then 里连续执行,而是交给系统调度?但系统只是FIFO,似乎没啥调度呀?

如何插入一个任务

可通过 dart:async 库:

  1. 通过Future 类,插入到 event queue 中
  2. 通过 scheduleMicrotask() 函数插入到 microtask queue.

scheduleMicrotask曾用名 runAsync ,现已废弃

使用Future 和 Timer 插入任务的区别是,前者对后者进行了封装,增加了任务完成检测及错误处理等,因此,建议优先考虑 Future 而不是裸调 Timer。

Future.delayed 的涵意是,延时多少秒后,把任务插入队列(而非执行任务)

Tips: web app中绘制动画时,不应使用 Future(Timer/Stream),应该使用 animationFrame (指向 requestAnimatorFrame)

几个要点:

  1. Future 的 then 参数里传递的函数会被直接执行而非插入队列

  2. 如果 Future 对象已经被计算完毕,尔后 then 才被调用,那系统会在 microtask queue 中插入一个任务,以执行 then 参数所指向的函数。

  3. Future 和 Future.delay 仅做入列操作而非直接执行任务。

  4. Future.value 会在microtask queue 中插入任务回调。

  5. Future.sync 构造函数会直接执行参数指向函数,并入列 microtask 。

This post is licensed under CC BY 4.0 by the author.