Dart 异步(一)线程

2023-01-08  本文已影响0人  陆元伟

单线程事件队列

Dart程序是单线程+事件驱动方式运行,Dart代码的运行就是在不停的在处理一个又一个的事件
而 Dart 的单线程跟 Java 的完全不同,虽然都是单线程,但是 Dart 分为三个形式:

主线程
微任务队列
事件任务队列

主线程

跟 Java 一样,具有唯一性,也就是从main()开始的线程。

微任务队列

表示一个短时间内就会完成的异步任务,它的优先级比事件队列高。
主要是通过scheduleMicrotask进行调度。

事件任务队列

包含所有的外来事件,比如:I/O、手势、绘图等。,例如:I/O 事件任务、Timer 事件任务等。

Flutter 任务队列的执行图:


图片.png

优先级

1 主线程 > 微任务队列 > 事件任务队列。

所以,在 Dart 单线程中,会优先执行完主线程,在执行主线程的过程中,若发现微任务或者事件任务,就会将他们放入相应的任务队列中,然后就会一个个执行完微任务队列里面的微任务,其次再去执行事件任务队列里面的事件任务。

示例代码如下

     Timer.run(() {
       print('Timer run');
     });
     scheduleMicrotask(() {
       print('scheduleMicrotask run');
     });
      print('main run');

打印输出,可以看出,即使我们把事件任务队列放在最前面,它也是最后执行

main run
scheduleMicrotask run
Timer run

Process finished with exit code 0

再来个复杂例子,在事件任务里面添加微任务,在微任务里面添加事件任务

 //主线程
    print('main start!');
    //事件任务
    Timer.run(() {
      print('事件任务 start!');
      //子微任务
      scheduleMicrotask(() {
        print('子微任务执行!');
      });
      print('事件任务 end!');
    });
    //微任务
    scheduleMicrotask(() {
      print('微任务 start!');
      //子事件任务
      Timer.run(() {
        print('微任务中子事件任务执行!');
      });
      scheduleMicrotask((){
        print('微任务微子事件任务执行!');
      });
      print('微任务 end!');
    });
    print('main end!');

输出

main start!
main end!
微任务 start!
微任务 end!
微任务微子事件任务执行!
事件任务 start!
事件任务 end!
子微任务执行!
微任务中子事件任务执行!

首先是main任务执行完,然后再执行微任务,等微任务完成后,再执行事件任务,等事件任务执行完,再微任务,事件任务。

添加sleep延迟

  //主线程
      print('main start!');
      sleep(Duration(seconds: 2));
      //微任务
      scheduleMicrotask((){
        print('微任务 start!');
        sleep(Duration(seconds: 2));
        //子事件任务
        Timer.run(() {
          print('子事件任务执行!');
          sleep(Duration(seconds: 2));
        });
        print('微任务 end!');
      });
      //事件任务
      Timer.run(() {
        print('事件任务 start!');
        sleep(Duration(seconds: 2));
        //子微任务
        scheduleMicrotask((){
          print('子微任务执行!');
          sleep(Duration(seconds: 2));
        });
        print('事件任务 end!');
      });
      print('main end!');

输出如下,由于dart本来就是单线程,,隐藏sleep后,后面的任务都暂停了,因此任务顺序不变

main start!
main end!
微任务 start!
微任务 end!
事件任务 start!
事件任务 end!
子微任务执行!
子事件任务执行!

结论: 在 Dart 单线程中,主线程、微任务队列、事件任务队列是线性执行的,后面的队列必须等到前面队列都运行完后再执行

多线程

在一个页面中做耗时比较大的运算时,就算用了 async、await 异步处理,UI页面的动画还是会卡顿,因为还是在这个UI线程中做运算,异步只是你可以先做其他,等我这边有结果再返回,但是,我们的计算仍旧是在这个UI线程,仍会阻塞UI的刷新,异步只是在同一个线程的并发操作。所以这个时候就需要创建新的线程来执行耗时操作解决这个问题。

什么是 Isolate

Isolate 是 Dart 平台对线程的实现方案,但和普通 Thread 不同的是,isolate 拥有独立的内存,isolate 由线程和独立内存构成。正是由于 isolate 线程之间的内存不共享,所以 isolate 线程之间并不存在资源抢夺的问题,所以也不需要锁。通过 isolate 可以很好的利用多核 CPU,来进行大量耗时任务的处理。

print('main start');
    Isolate.spawn((msg){
      print('spawn 1');
      print(msg);
    },'1');
    Isolate.spawn((msg){
      print('spawn 2');
      print(msg);
    },'2');
    Isolate.spawn((msg){
      print('spawn 3');
      print(msg);
    },'3');
    print('main end');

输出如下(每次输出不一样)

main start
spawn 1
1
main end
spawn 3
3
spawn 2
2

Isolate 通信机制

isolate 之间的通信主要通过 SendPort和ReceivePort来进行。主要流程是:先初始化一个ReceivePort,然后调用listen就可以监听其他isolate发送的消息。
那其他isolate如何给当前isolate发送消息?答案就是获取到ReceivePort的sendPort。

ReceivePort:接收其他Isolate数据
SendPort:发送给其他Isolate数据

代码如下,由于生成Isolate的spawn方法第二个参数就是传递给Isolate的数据,因此我们把主isolate的SendPort发送过去,这样子isolate获取到主Isolate的SendPort,就可以调用sendPort的send方法给主Isolate发送数据,主Isolate就可以接收到。

那主Isolate除了创建时给子Isolate发送数据,后续如何给子Isolate继续发送数据呢?

这就要求子Isolate把它自己的SendPort发送给主Isolate

    print('main start');
    ReceivePort port = ReceivePort();
     Isolate iso = await Isolate.spawn<SendPort>((message) {
      print('子iso:发送消息gaga');
      message.send('gaga');
      print('子iso:发送成功');

      ReceivePort iosPort = ReceivePort();
      message.send(iosPort.sendPort);
      iosPort.listen((msg) {
        print('子iso:$msg');
      });
      print('子iso:发送port成功');
    }, port.sendPort);

    port.listen((message) {
      print('主iso:收到子iso $message');
      if(message is SendPort){
        message.send('主线程发送子线程消息');
        port.close();
      }
    });
 print('main end');

输出如下

main start
main end
子iso:发送消息gaga
子iso:发送成功
子iso:发送port成功
主iso:收到子iso gaga
主iso:收到子iso SendPort
子iso:主线程发送子线程消息

参考文章:
Dart单线程理解(与Java完全不同)
Flutter异步编程-Isolate
深入理解Flutter/Dart事件机制

上一篇 下一篇

猜你喜欢

热点阅读