咱们都知道 Dart 是一个单线程模型的语言,但这并不意味着它只支撑单线程,它也是支撑多线程的,可以运用 isolate 进行完成的。所以,这个单线程的说法具有迷惑性,实际状况下,其单线程的履行仅仅描述它的事情循环机制,至于 IO、网络等操作,仍是得放到 isolate 中。而 main 办法的履行,其实也是在一个 isolate 中。 所以,若是仅仅是说事情循环机制,运用 Java 也是相同可以模仿一个出来,仅仅 Android 提供了 Handler 机制去处理事情分发处理的问题,不需求咱们自己重新封装。

事情循环机制

简略来说,代码的履行是次序履行,从 main() 开始,等遇到 Microtask(微使命) 和 Timer(事情) 后,就分别用行列将其存储起来,当 main 办法履行完后,就会先去履行 Microtask 的音讯行列,再去履行 Timer 的音讯行列。

大致的流程如下所示:

Dart 的同步与异步

下面用这个简略的例子验证下:

void main(List<String> arguments) async{
  print('main start!');
  //微使命
  scheduleMicrotask((){
    print('Microtask 履行!第一次');
  });
  //事情使命
  Timer.run(() {
    print('Timer 履行!第一次');
    Timer.run(() {
      print('Timer 履行!第2次');
    });
    scheduleMicrotask((){
      print('Microtask 履行!第2次');
    });
  });
  print('main end!');
}

输出成果:

main start!
main end!
Microtask 履行!第一次
Timer 履行!第一次
Microtask 履行!第2次
Timer 履行!第2次
Process finished with exit code 0

有两点要留意:

  • 当在 Timer 中履行 scheduleMicrotaskTimer.run 的时候,会往相应的行列添加数据,直到 Microtask QueueEvent Queue 都为空时,才会退出程序履行。
  • 正常状况下,咱们运用 Timer.run 即可,尽量不要运用 scheduleMicrotask,因为 Microtask 在 flutter 中会承载触摸事情等优先级较高的事情处理。

Timer 具体的存储位置在:sdk > lib > _internal > vm > lib > timer_impl.dart > _Timer_impl 中:

class _TimerHeap {
  List<_Timer> _list;
  int _used = 0;
  _TimerHeap([int initSize = 7])
      : _list = List<_Timer>.filled(initSize, _Timer._sentinelTimer);
  
}

Future

咱们先来看个栗子:

void main() {
  print('main start');
  Future.delayed(Duration(seconds: 3), (){
    print('Future run');
  });
  print('main end');
}
main start
main end
Future run
Process finished with exit code 0

咱们可以看到 Future 相比于 main() 确实延期履行了,就像敞开了一个线程相同,那咱们该怎么去了解 Future?

这,咱们可以从源码方面进行剖析:

  factory Future.delayed(Duration duration, [FutureOr<T> computation()?]) {
    if (computation == null && !typeAcceptsNull<T>()) {
      throw ArgumentError.value(
          null, "computation", "The type parameter is not nullable");
    }
    _Future<T> result = new _Future<T>();
    new Timer(duration, () {
      if (computation == null) {
        result._complete(null as T);
      } else {
        try {
          result._complete(computation());
        } catch (e, s) {
          _completeWithErrorCallback(result, e, s);
        }
      }
    });
    return result;
  }

很简略,其实它仅仅包装成了 Timer,所以,并没有敞开一个新线程,而是依照了事情循环机制,放到 Event Queue中,优先履行了 main()

那咱们又该怎么去了解 await 和 async ?

相同,咱们再看看一个栗子:

void main() async{
  print('main start');
  final data = await getNetworkData();
  print('main end. network data: $data');
}
Future<String> getNetworkData(){
  return Future.delayed(Duration(seconds: 3), () => 'I am robot');
}
main start
main end. network data: I am robot
Process finished with exit code 0

咱们看日志输出作用,感觉 await 把 main() 堵塞了,只要成功获取得到 Future 的值才会持续履行下去。

可是,其实这儿并没有进行堵塞,而是运用了 select 的概念,也就对错堵塞式等候。

这儿就可能有人有疑问了?这堵塞式和非堵塞式有什么区别?

咱们都知道,系统的 CPU 时刻片的分配最小单位为线程,而程序的功能仅仅线程的代码履行而已,堵塞式便是当时线程放弃当时时刻片,进入等候状况,交由其它线程履行完,再进行唤醒,再等候时刻片进行履行;而非堵塞式则是没有进入等候状况,而是经过自旋的方法进行履行,等候其它使命完成,它再持续履行下去。

自旋的最简略了解便是:

var status = true;
while(status){}

等候更改 status 值然后跳出当时循环。

isolate

isolate 简略可以了解为一个线程,和 Java 的 Thread 类似,可是他们之间却还有很大的一个区别,咱们先来看看 Java 的运转数据区:

Dart 的同步与异步

这儿有一个很明显的特点,便是线程具有同享的区域,特别是堆,阐明线程之间的同享只要传堆的引证即可,无需真正拷贝数据过去,可是,这样就会呈现一个问题,那便是同享的数据可能呈现不安全的状况,即多线程可以一起修正同一份数据,由此,Java 延伸出锁的概念,Synchronized、ReentrantLock 等等便孕育而生,便是为了处理多线程并发问题。

Dart 为了避免这种状况,运用了别的一种概念:

Dart 的同步与异步

也便是 isolate 之间尽可能不保持联系,他们之间的数据传输都是经过 port 传输实在的数据,而不是传目标的引证。

一起因为 isolate 的相对独立性,所以 Dart 不需求锁的概念,并且在内存收回上,无需 STW(Stop the world),直接纳回 isolate 中悉数资源即可。相同的,咱们也来看看一个栗子:

void main() async{
  print('main start');
  ReceivePort receivePort = ReceivePort();
  Isolate.spawn(getNetworkData,["getUserInfo",receivePort.sendPort]);
  //监听接纳音讯
  receivePort.listen((message) {
    print("收到音讯:$message");
  });
  print('main end');
}
void getNetworkData(var message){
  // 获取传入的数据
  String path = message[0];
  SendPort sendPort = message[1];
  sendPort.send('path : $path, info : UserInfo');
}

输出的成果为:

main start
main end
收到音讯:path : getUserInfo, info : UserInfo

咱们可以看出,isolate 是经过 ReceivePort 和 SendPort 来进行数据的发送和接纳。

别的,有一点需求留意,便是该程序运转后,并没有退出,ReceivePort 仍在等候音讯,所以,咱们需求当令将其关闭:

receivePort.listen((message) {
  print("收到音讯:$message");
  receivePort.close();
});